Skip to content

Commit a244abe

Browse files
committed
add solve helper function for minizinc
1 parent 03dbe02 commit a244abe

2 files changed

Lines changed: 105 additions & 95 deletions

File tree

Lines changed: 97 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import { onLoad } from '../../utils/utils';
12
import { minizincUrl } from '../../vendors';
23

34
declare const livecodes: {
45
minizinc: {
5-
run: (data: { dzn?: string; json?: string; config?: MiniZincConfig } | 'init') => Promise<any>;
6+
init: () => Promise<void>;
7+
solve: (data?: MiniZincData) => any;
8+
run: (data?: MiniZincData) => Promise<any>;
69
getSolvers: () => Promise<string[]>;
710
};
811
};
@@ -18,101 +21,100 @@ interface MiniZincConfig {
1821
};
1922
}
2023

21-
let hasRun = false;
22-
const pageLoaded = new Promise((resolve) => {
23-
if (document.readyState === 'complete' || document.readyState === 'interactive') {
24-
resolve(undefined);
25-
} else {
26-
window.addEventListener('load', resolve);
27-
}
28-
});
24+
interface MiniZincData {
25+
dzn?: string;
26+
json?: string;
27+
config?: MiniZincConfig;
28+
}
2929

30+
let MiniZinc: any;
31+
let hasRun = false;
32+
const pageLoaded = new Promise((resolve) => onLoad(resolve));
3033
const modPromise = import(minizincUrl);
3134

35+
const run = (data: MiniZincData = {}) => {
36+
if (!MiniZinc) {
37+
throw new Error('MiniZinc is not initialized. await livecodes.minizinc.init() first.');
38+
}
39+
hasRun = true;
40+
if (typeof data === 'string') data = {};
41+
const { dzn = '', json = '', config = {} } = data;
42+
let code = '';
43+
const scripts = document.querySelectorAll('script[type="text/minizinc"]');
44+
scripts.forEach((script) => (code += script.innerHTML + '\n'));
45+
const model = new MiniZinc.Model();
46+
model.addFile('playground.mzn', code);
47+
if (dzn) model.addFile('playground.dzn', dzn);
48+
if (json) model.addFile('playground.json', json);
49+
return model.solve({
50+
jsonOutput: config.jsonOutput ?? false,
51+
options: {
52+
solver: 'gecode',
53+
'time-limit': 10000,
54+
...config.options,
55+
},
56+
});
57+
};
58+
3259
livecodes.minizinc = {
33-
run: async (data: { dzn?: string; json?: string; config?: MiniZincConfig } | 'init' = {}) => {
34-
if (data === 'init' && hasRun) return;
35-
const { dzn = '', json = '', config = {} } = data === 'init' ? {} : data;
36-
hasRun = true;
60+
init: async () => {
61+
if (MiniZinc) return;
3762
await pageLoaded;
38-
let code = '';
39-
const scripts = document.querySelectorAll('script[type="text/minizinc"]');
40-
scripts.forEach((script) => (code += script.innerHTML + '\n'));
41-
42-
const MiniZinc = await modPromise;
63+
MiniZinc = await modPromise;
64+
},
65+
solve: (data) => run(data),
66+
run: async (data) => {
67+
await livecodes.minizinc.init();
4368
return new Promise((resolve) => {
44-
try {
45-
const minizincConfig = config;
46-
const model = new MiniZinc.Model();
47-
model.addFile('playground.mzn', code);
48-
if (dzn) model.addFile('playground.dzn', dzn);
49-
if (json) model.addFile('playground.json', json);
50-
51-
const solve = model.solve({
52-
jsonOutput: minizincConfig.jsonOutput ?? false,
53-
options: {
54-
solver: 'gecode',
55-
'time-limit': 10000,
56-
...minizincConfig.options,
57-
},
58-
});
59-
60-
const errors: any[] = [];
61-
solve.on('error', (error: any) => {
62-
// eslint-disable-next-line no-console
63-
console.error(error);
64-
errors.push(error);
65-
});
66-
solve.on('exit', (msg: any) => {
67-
if (msg.code === 0) return;
68-
if (errors.length) {
69-
resolve({
70-
status: 'ERROR',
71-
errors,
72-
});
73-
} else {
74-
resolve({
75-
status: 'ERROR',
76-
errors: [
77-
{
78-
type: 'error',
79-
message: `Process finished with non-zero exit code ${msg.code}.`,
80-
},
81-
],
82-
});
83-
}
84-
});
85-
solve.then((result: any) => {
86-
const statusMap: any = {
87-
ALL_SOLUTIONS: '==========',
88-
OPTIMAL_SOLUTION: '==========',
89-
UNSATISFIABLE: '=====UNSATISFIABLE=====',
90-
UNSAT_OR_UNBOUNDED: '=====UNSATorUNBOUNDED=====',
91-
UNBOUNDED: '=====UNBOUNDED=====',
92-
UNKNOWN: '=====UNKNOWN=====',
93-
ERROR: '=====ERROR=====',
94-
};
95-
const status = statusMap[result.status] || '';
96-
const output =
97-
result.solution?.output?.default ??
98-
result.solution?.output?.dzn ??
99-
result.solution?.output?.json ??
100-
result.solution?.output?.raw ??
101-
result.solution?.output ??
102-
'';
103-
const msg = typeof output === 'string' ? output + status : output;
104-
// eslint-disable-next-line no-console
105-
console.log(msg);
106-
resolve(result);
107-
});
108-
} catch (err) {
69+
const solve = run(data);
70+
const errors: any[] = [];
71+
solve.on('error', (error: any) => {
72+
// eslint-disable-next-line no-console
73+
console.error(error);
74+
errors.push(error);
75+
});
76+
solve.on('exit', (msg: any) => {
77+
if (msg.code === 0) return;
78+
if (errors.length) {
79+
resolve({
80+
status: 'ERROR',
81+
errors,
82+
});
83+
} else {
84+
resolve({
85+
status: 'ERROR',
86+
errors: [
87+
{
88+
type: 'error',
89+
message: `Process finished with non-zero exit code ${msg.code}.`,
90+
},
91+
],
92+
});
93+
}
94+
});
95+
solve.then((result: any) => {
96+
const statusMap: any = {
97+
ALL_SOLUTIONS: '==========',
98+
OPTIMAL_SOLUTION: '==========',
99+
UNSATISFIABLE: '=====UNSATISFIABLE=====',
100+
UNSAT_OR_UNBOUNDED: '=====UNSATorUNBOUNDED=====',
101+
UNBOUNDED: '=====UNBOUNDED=====',
102+
UNKNOWN: '=====UNKNOWN=====',
103+
ERROR: '=====ERROR=====',
104+
};
105+
const status = statusMap[result.status] || '';
106+
const output =
107+
result.solution?.output?.default ??
108+
result.solution?.output?.dzn ??
109+
result.solution?.output?.json ??
110+
result.solution?.output?.raw ??
111+
result.solution?.output ??
112+
'';
113+
const msg = typeof output === 'string' ? output + status : output;
109114
// eslint-disable-next-line no-console
110-
console.error(err);
111-
resolve({
112-
status: 'ERROR',
113-
errors: [err],
114-
});
115-
}
115+
console.log(msg);
116+
resolve(result);
117+
});
116118
});
117119
},
118120
getSolvers: async () => {
@@ -121,10 +123,10 @@ livecodes.minizinc = {
121123
},
122124
};
123125

124-
if (document.readyState === 'complete' || document.readyState === 'interactive') {
125-
livecodes.minizinc.run('init');
126-
} else {
127-
window.addEventListener('load', () => {
128-
livecodes.minizinc.run('init');
126+
onLoad(() => {
127+
livecodes.minizinc.init().then(() => {
128+
// run only if user code has not called `run` or `solve`
129+
if (hasRun) return;
130+
livecodes.minizinc.run();
129131
});
130-
}
132+
});

src/livecodes/utils/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,14 @@ export const addProp = /* @__PURE__ */ (
685685
addProp(obj[first] as Record<string, unknown>, rest.join('.'), value);
686686
};
687687

688+
export const onLoad = /* @__PURE__ */ (fn: (...args: any[]) => any) => {
689+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
690+
fn();
691+
} else {
692+
window.addEventListener('load', fn);
693+
}
694+
};
695+
688696
export const predefinedValues = {
689697
APP_VERSION: process.env.VERSION || '',
690698
SDK_VERSION: process.env.SDK_VERSION || '',

0 commit comments

Comments
 (0)