-
Notifications
You must be signed in to change notification settings - Fork 1
/
app.py
208 lines (184 loc) · 8.04 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import numpy
from flask import Flask, render_template, request, jsonify, send_from_directory
import ctypes
import atexit
import json
import datetime
import csv
import os
app = Flask(__name__)
# instructions for the 2d array from https://stackoverflow.com/questions/58727931/how-to-pass-a-2d-array-from-python-to-c
# the "bad" solution in the question worked for me because I need a 2d array and not a pointer
# c libraries in python from https://docs.python.org/3/extending/extending.html
# Setup for the cube solver
# Give a ctypes 2d int array an easier name
array_2d_int = numpy.ctypeslib.ndpointer(
dtype=ctypes.c_int, ndim=2, flags='CONTIGUOUS')
# Load the solver library
solver = ctypes.CDLL("./bin/libcubesolver.so")
# Set the argument types. String for one, 2d int array for the rest
solver.setup.argtypes = [ctypes.c_char_p]
solver.run_algorithm.argtypes = [array_2d_int, ctypes.c_char_p]
solver.solve_safe.argtypes = [array_2d_int]
solver.print_cube.argtypes = [array_2d_int]
solver.validate.argtypes = [array_2d_int]
solver.solve_cross_safe.argtypes = [array_2d_int]
solver.solve_f2l_safe.argtypes = [array_2d_int]
solver.solve_oll_safe.argtypes = [array_2d_int]
solver.solve_pll_safe.argtypes = [array_2d_int]
# Set the argument types for the functions that return something else than an int. Basically all char arrays.
solver.solve_safe.restype = ctypes.c_char_p
solver.solve_cross_safe.restype = ctypes.c_char_p
solver.solve_f2l_safe.restype = ctypes.c_char_p
solver.solve_oll_safe.restype = ctypes.c_char_p
solver.solve_pll_safe.restype = ctypes.c_char_p
# List of possible errors.
errors = ["Unable to load algorithms",
"Invalid color combination",
"Bug in cross algorithm. Please report scramble to d4m4s74 in discord\n",
"Bug in f2l algorithm. Please report scramble to d4m4s74 in discord\n",
"OLL Parity.\nOne of the cubies is flipped or twisted\n",
"PLL Parity\nTwo cubies are switched\n"]
solver.setup("data".encode('utf-8'))
# The order of this cube is assuming green is 0, red is 1, etc. white is 4, yellow is 5.
solvedcube = [[0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3],
[4, 4, 4, 4, 4, 4, 4, 4, 4],
[5, 5, 5, 5, 5, 5, 5, 5, 5]]
# stolen from stackoverflow: decomment. Which removes comments from CSVs
# url: https://stackoverflow.com/questions/14158868/python-skip-comment-lines-marked-with-in-csv-dictreader
def decomment(csvfile):
for row in csvfile:
raw = row.split('#')[0].strip()
if raw:
yield raw
# Function that gets the steps of solving a cube and returns a list of dicts
def getsteps(cube):
# empty step list
steps = []
# every step could generate an AttributeError because it's trying to decode null, this should not occur because my cube.js file validates everything side.
# because I don't need to return an error here, I just put everything in a try except
try:
# solve the cross
alg = solver.solve_cross_safe(cube).decode("utf-8")
# add the cross to the steps
steps.append({'step': "Cross", 'name': "",
'algorithms': alg.strip().split("\n")})
# solve the F2L
alg = solver.solve_f2l_safe(cube).decode("utf-8")
# add the F2L to the steps
steps.append({'step': "F2L", 'name': "",
'algorithms': alg.strip().split("\n")})
# solve the OLL
alg = solver.solve_oll_safe(cube).decode("utf-8")
# add the OLL to the steps
steps.append({'step': "OLL", 'name': alg.split("\n")[
0], 'algorithms': alg.strip().split("\n")[1:]})
# solve the PLL
alg = solver.solve_pll_safe(cube).decode("utf-8")
steps.append({'step': "PLL", 'name': alg.split("\n")[
0], 'algorithms': alg.strip().split("\n")[1:]})
# free the C strings
solver.free_strings()
return steps
except:
solver.free_strings()
# in case of an exception, there might be an error with my validator, or my js file.
with open("data/errors.txt", "a") as errorFile: # save the error
now = datetime.datetime.now()
errorFile.write(now.strftime(
"%Y-%m-%d %H:%M:%S getsteps: ") + jsonify(cube))
return list()
def cleanup():
solver.cleanup_last_layer()
solver.free_strings()
atexit.register(cleanup)
# Only used for testing purposes: generates solution from scramble algorithm
@app.route('/solver')
def solverInterface():
scramble = request.args.get("scramble", "").encode('utf-8')
if not scramble:
return render_template("solver.html", steps=list())
# Create a 2d array containing c ints from the aforementioned default solved cube
cube = numpy.array(solvedcube).astype(ctypes.c_int)
solver.run_algorithm(cube, scramble)
steps = getsteps(cube)
return render_template("solver.html", steps=steps)
@app.route("/api/solver", methods=['GET', 'POST'])
def solverJSON():
# gets scramble algorithm, and pattern from get or post
if request.method == 'POST':
scramble = request.form.get("scramble", "").encode('utf-8')
patternJSON = request.form.get("pattern", "")
else:
scramble = request.args.get("scramble", "").encode('utf-8')
patternJSON = request.form.get("pattern", "")
# if there are no scramble and pattern json, return an empty list
if not scramble and not patternJSON:
return jsonify(list())
# if there is a pattern, turn it into a numpy array
if patternJSON:
pattern = json.loads(patternJSON)
cube = numpy.array(pattern).astype(ctypes.c_int)
# if not, take a solved cube
else:
cube = numpy.array(solvedcube).astype(ctypes.c_int)
# scramble the cube using the given scramble algorithm
solver.run_algorithm(cube, scramble)
# generate a solution
steps = getsteps(cube)
# return the solution
return jsonify(steps)
# this function basically solves the cube, just to see if it's valid.
@app.route("/api/validator", methods=['GET', 'POST'])
def validatorJSON():
if request.method == 'POST':
patternJSON = request.form.get("pattern", "")
else:
patternJSON = request.args.get("pattern", "")
if not patternJSON:
return jsonify(1) # 1 stands for no pattern
pattern = json.loads(patternJSON)
# Create a 2d array containing c ints from the aforementioned cube
cube = numpy.array(pattern).astype(ctypes.c_int)
if solver.validate(cube) == 0: # If it's an invalid cube
return jsonify(2)
# This only happens if there's a bug in the solver
if solver.solve_cross_safe(cube) is None:
solver.free_strings()
with open("data/errors.txt", "a") as errorFile: # save the error
now = datetime.datetime.now()
errorFile.write(now.strftime(
"%Y-%m-%d %H:%M:%S CROSS: ") + patternJSON)
return jsonify(5)
# This only happens if there's a bug in the solver
if solver.solve_f2l_safe(cube) is None:
solver.free_strings()
with open("data/errors.txt", "a") as errorFile: # save the error
now = datetime.datetime.now()
errorFile.write(now.strftime(
"%Y-%m-%d %H:%M:%S F2L: ") + patternJSON)
return jsonify(5)
if solver.solve_oll_safe(cube) is None:
solver.free_strings()
return jsonify(3)
if solver.solve_pll_safe(cube) is None:
solver.free_strings()
return jsonify(4)
solver.free_strings()
return jsonify(0)
@app.route('/')
@app.route("/cube")
def cube():
with open('data/patterns.csv', 'r') as fp:
patterns = csv.DictReader(decomment(fp))
return render_template('cube.html', patterns=patterns)
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')
if __name__ == '__main__':
app.debug = True
app.run(host='127.0.0.1', port=5000)