aboutsummaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-08-14 13:32:19 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-08-14 13:32:19 +0200
commitc835bf15da3ce39b856222d352c800f6ebd51acf (patch)
tree22f44fb6e766d9758a72182cef1dca7131e7a3a7 /python
parent325778cb5f4b169ef7e197e179cfb889808d10c2 (diff)
downloadnissy-core-c835bf15da3ce39b856222d352c800f6ebd51acf.tar.gz
nissy-core-c835bf15da3ce39b856222d352c800f6ebd51acf.zip
Added attempt to make pause / resume work with python module
Diffstat (limited to 'python')
-rw-r--r--python/examples/pause_resume.py35
-rw-r--r--python/examples/solve.py6
-rw-r--r--python/nissy_module.c67
3 files changed, 98 insertions, 10 deletions
diff --git a/python/examples/pause_resume.py b/python/examples/pause_resume.py
new file mode 100644
index 0000000..5daebeb
--- /dev/null
+++ b/python/examples/pause_resume.py
@@ -0,0 +1,35 @@
1# This example is meant to test the pause / resume / stop mechanism
2# See the solve.py example for more details on how this works
3
4import sys, os, time
5
6sys.path.append(os.getcwd())
7sys.path.append(os.getcwd() + os.path.sep + "python")
8import nissy
9
10# Function used to pause, resume and kill the solver
11t0 = time.time()
12def poll():
13 t = time.time() - t0
14 if t < 3:
15 print("[{}s] pausing for the first 3 seconds".format(t))
16 return nissy.status_pause
17 if t < 6:
18 print("[{}s] Letting the solver run for 3 seconds".format(t))
19 return nissy.status_run
20 print("[{}s] Too much time has passed, giving up".format(t))
21 return nissy.status_stop
22
23solver = "h48h0k4"
24datapath = "tables" + os.path.sep + solver
25if os.path.exists(datapath):
26 data = bytearray(open(datapath, "rb").read())
27else:
28 data = nissy.gendata("h48h0k4")
29 print("Generated data will NOT be persisted")
30
31# Scramble is 19 moves optimal, should take a while with the h48h0k4 solver
32scramble = "R' U' F D R F2 D L F D2 F2 L' U R' L2 D' R2 F2 R2 D L2 U2 R' U' F"
33cube = nissy.applymoves(nissy.solved_cube, scramble);
34
35nissy.solve(cube, solver, nissy.nissflag_normal, 0, 9, 3, 20, 4, data, poll)
diff --git a/python/examples/solve.py b/python/examples/solve.py
index 75e7914..c2c992b 100644
--- a/python/examples/solve.py
+++ b/python/examples/solve.py
@@ -3,12 +3,10 @@
3# Run "./build.sh python", then run this from either the top-level directory 3# Run "./build.sh python", then run this from either the top-level directory
4# of the nissy-core repo or from the python subdirectory. 4# of the nissy-core repo or from the python subdirectory.
5 5
6# Append the directories to the python path so we can load the module 6# Append the directories to the python path and import
7import sys, os 7import sys, os
8sys.path.append(os.getcwd()) 8sys.path.append(os.getcwd())
9sys.path.append(os.getcwd() + os.path.sep + "python") 9sys.path.append(os.getcwd() + os.path.sep + "python")
10
11# Import with a nicer name
12import nissy 10import nissy
13 11
14# Choose the solver you prefer 12# Choose the solver you prefer
@@ -26,7 +24,7 @@ else:
26cube = nissy.applymoves(nissy.solved_cube, "U F R2"); 24cube = nissy.applymoves(nissy.solved_cube, "U F R2");
27 25
28# Solve! 26# Solve!
29solutions = nissy.solve(cube, solver, nissy.nissflag_normal, 0, 9, 3, 20, 4, data) 27solutions = nissy.solve(cube, solver, nissy.nissflag_normal, 0, 9, 3, 20, 4, data, None)
30 28
31# Print the solutions, one per line 29# Print the solutions, one per line
32print("Found ", len(solutions), " solutions:") 30print("Found ", len(solutions), " solutions:")
diff --git a/python/nissy_module.c b/python/nissy_module.c
index bff4ead..23ff589 100644
--- a/python/nissy_module.c
+++ b/python/nissy_module.c
@@ -1,3 +1,9 @@
1/*
2This version of the python module includes an attempt to use the callback
3functions for pausing / stopping / resuming a solve. It probably cannot
4work until PEP 703 (freethreading python) is implemented.
5*/
6
1#define PY_SSIZE_T_CLEAN 7#define PY_SSIZE_T_CLEAN
2#include <Python.h> 8#include <Python.h>
3#include <stdbool.h> 9#include <stdbool.h>
@@ -6,6 +12,11 @@
6 12
7#define MAX_SOLUTIONS_SIZE 250000 13#define MAX_SOLUTIONS_SIZE 250000
8 14
15typedef struct {
16 PyObject *callback;
17 PyThreadState *thread_state;
18} thread_callback_t;
19
9static bool 20static bool
10check_error(long long err) 21check_error(long long err)
11{ 22{
@@ -84,6 +95,32 @@ long_result(long long result)
84 return PyLong_FromLong(result); 95 return PyLong_FromLong(result);
85} 96}
86 97
98static int
99callback_wrapper(void *data)
100{
101 thread_callback_t *arg = data;
102 int r;
103 PyObject *result;
104 PyGILState_STATE gstate;
105
106 if (arg->callback != NULL && PyCallable_Check(arg->callback) == 1) {
107
108PyEval_RestoreThread(arg->thread_state);
109gstate = PyGILState_Ensure();
110 result = PyObject_CallNoArgs(data);
111PyGILState_Release(gstate);
112arg->thread_state = PyEval_SaveThread();
113
114 if (result == NULL)
115 return 0;
116 Py_XDECREF(result);
117 r = PyLong_AsInt(result);
118 return r >= 0 && r <= 2 ? r : 0;
119 } else {
120 return NISSY_STATUS_RUN;
121 }
122}
123
87PyDoc_STRVAR(inverse_doc, 124PyDoc_STRVAR(inverse_doc,
88"inverse(cube)\n" 125"inverse(cube)\n"
89"--\n\n" 126"--\n\n"
@@ -288,7 +325,7 @@ checkdata(PyObject *self, PyObject *args)
288 325
289PyDoc_STRVAR(solve_doc, 326PyDoc_STRVAR(solve_doc,
290"solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions," 327"solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions,"
291" optimal, threads, data)\n" 328" optimal, threads, data, callback)\n"
292"--\n\n" 329"--\n\n"
293"Solves the given 'cube' with the given 'solver' and other parameters." 330"Solves the given 'cube' with the given 'solver' and other parameters."
294"See the documentation for libnissy (in nissy.h) for details.\n" 331"See the documentation for libnissy (in nissy.h) for details.\n"
@@ -302,6 +339,9 @@ PyDoc_STRVAR(solve_doc,
302" - optimal: the largest number of moves from the shortest solution\n" 339" - optimal: the largest number of moves from the shortest solution\n"
303" - threads: the number of threads to use (0 for default)\n" 340" - threads: the number of threads to use (0 for default)\n"
304" - data: a bytearray containing the data for the solver\n" 341" - data: a bytearray containing the data for the solver\n"
342" - callback: a function that returns 0 (run), 1 (stop) or 2 (pause).\n"
343" Polled by the solver to determine if the user requested the\n"
344" to pause or stop the solve. Not used by all solvers.\n"
305"\n" 345"\n"
306"Returns: a list with the solutions found\n" 346"Returns: a list with the solutions found\n"
307); 347);
@@ -315,17 +355,29 @@ solve(PyObject *self, PyObject *args)
315 char solutions[MAX_SOLUTIONS_SIZE]; 355 char solutions[MAX_SOLUTIONS_SIZE];
316 long long stats[NISSY_SIZE_SOLVE_STATS]; 356 long long stats[NISSY_SIZE_SOLVE_STATS];
317 PyByteArrayObject *data; 357 PyByteArrayObject *data;
358 PyObject *callback;
359 thread_callback_t callback_data;
318 360
319 if (!PyArg_ParseTuple(args, "ssIIIIIIY", &cube, &solver, &nissflag, 361 if (!PyArg_ParseTuple(args, "ssIIIIIIYO", &cube, &solver, &nissflag,
320 &minmoves, &maxmoves, &maxsolutions, &optimal, &threads, &data)) 362 &minmoves, &maxmoves, &maxsolutions, &optimal, &threads, &data,
363 &callback))
321 return NULL; 364 return NULL;
322 365
323 Py_BEGIN_ALLOW_THREADS 366printf("Checking callback here:\n");
367PyObject *rrr = PyObject_CallNoArgs(callback);
368printf("result: %d\n", PyLong_AsInt(rrr));
369
370 callback_data.callback = callback;
371 callback_data.thread_state = PyEval_SaveThread();
372 //Py_BEGIN_ALLOW_THREADS
373
324 result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves, 374 result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves,
325 maxsolutions, optimal, threads, data->ob_alloc, 375 maxsolutions, optimal, threads, data->ob_alloc,
326 (unsigned char *)data->ob_bytes, MAX_SOLUTIONS_SIZE, solutions, 376 (unsigned char *)data->ob_bytes, MAX_SOLUTIONS_SIZE, solutions,
327 stats, NULL, NULL); 377 stats, callback_wrapper, &callback_data);
328 Py_END_ALLOW_THREADS 378
379 PyEval_RestoreThread(callback_data.thread_state);
380 //Py_END_ALLOW_THREADS
329 381
330 return stringlist_result(result, solutions); 382 return stringlist_result(result, solutions);
331} 383}
@@ -461,6 +513,9 @@ PyMODINIT_FUNC PyInit_nissy(void) {
461 PyModule_AddIntConstant(module, "nissflag_mixed", NISSY_NISSFLAG_MIXED); 513 PyModule_AddIntConstant(module, "nissflag_mixed", NISSY_NISSFLAG_MIXED);
462 PyModule_AddIntConstant(module, "nissflag_linear", NISSY_NISSFLAG_LINEAR); 514 PyModule_AddIntConstant(module, "nissflag_linear", NISSY_NISSFLAG_LINEAR);
463 PyModule_AddIntConstant(module, "nissflag_all", NISSY_NISSFLAG_ALL); 515 PyModule_AddIntConstant(module, "nissflag_all", NISSY_NISSFLAG_ALL);
516 PyModule_AddIntConstant(module, "status_run", NISSY_STATUS_RUN);
517 PyModule_AddIntConstant(module, "status_stop", NISSY_STATUS_STOP);
518 PyModule_AddIntConstant(module, "status_pause", NISSY_STATUS_PAUSE);
464 519
465 return module; 520 return module;
466} 521}

Generated with cgit - Back to sebastiano.tronto.net