├── .gitignore
├── clean.sh
├── cryptominisat
├── glucose_static
├── hexiom_config.py
├── hexiom_solve.py
├── levels
├── level00.txt
├── level01.txt
├── level02.txt
├── level03.txt
├── level04.txt
├── level05.txt
├── level06.txt
├── level07.txt
├── level08.txt
├── level09.txt
├── level10.txt
├── level11.txt
├── level12.txt
├── level13.txt
├── level14.txt
├── level15.txt
├── level16.txt
├── level17.txt
├── level18.txt
├── level19.txt
├── level20.txt
├── level21.txt
├── level22.txt
├── level23.txt
├── level24.txt
├── level25.txt
├── level26.txt
├── level27.txt
├── level28.txt
├── level29.txt
├── level30.txt
├── level31.txt
├── level32.txt
├── level33.txt
├── level34.txt
├── level35.txt
├── level36.txt
├── level37.txt
├── level38.txt
├── level39.txt
└── level40.txt
├── lingeling
├── readme.md
├── sat_in
└── .placeholder
├── sat_out
└── .placeholder
└── scratch
└── .placeholder
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
3 | sat_in/*
4 | sat_out/*
5 | scratch/*
6 |
--------------------------------------------------------------------------------
/clean.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | rm ./*.pyc
3 | rm ./sat_in/*.cnf
4 | rm ./sat_out/*.txt
5 | rm ./scratch/*.txt
6 |
--------------------------------------------------------------------------------
/cryptominisat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomg/hexiom/be312775d6328b77c4b6a45c1556bf022dbef4a3/cryptominisat
--------------------------------------------------------------------------------
/glucose_static:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomg/hexiom/be312775d6328b77c4b6a45c1556bf022dbef4a3/glucose_static
--------------------------------------------------------------------------------
/hexiom_config.py:
--------------------------------------------------------------------------------
1 |
2 | LEVEL_INPUT_FILENAME_PATTERN = (lambda n: 'levels/level%02d.txt' % n )
3 | SAT_INPUT_FILENAME_PATTERN = (lambda n: 'sat_in/level%02d.cnf' % n )
4 | SAT_OUTPUT_FILENAME_PATTERN = (lambda n: 'sat_out/level%02d.txt' % n )
5 |
6 | NAMED_CNF_INPUT_FILE = 'scratch/formula.txt'
7 | NAMED_CNF_RESULT_FILE = 'scratch/result.txt'
8 |
9 | import subprocess
10 |
11 | def in_file_out_file(exe_name):
12 | ''' Run a minisat style solver'''
13 | def solve(infilename, outfilename):
14 | return subprocess.call(
15 | [exe_name, infilename, outfilename]
16 | )
17 | return solve
18 |
19 | def in_file_out_pipe(exe_name):
20 | ''' Run a precosat style solver'''
21 | def solve(infilename, outfilename):
22 | with open(outfilename, 'w') as fil:
23 | return subprocess.call(
24 | [exe_name, infilename],
25 | stdout=fil
26 | )
27 | return solve
28 |
29 | #SAT_SOLVE = in_file_out_pipe('./lingeling')
30 | #SAT_SOLVE = in_file_out_file('./cryptominisat')
31 | SAT_SOLVE = in_file_out_file('./glucose_static')
32 |
--------------------------------------------------------------------------------
/hexiom_solve.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # - * - coding:utf-8 - * -
3 |
4 | ############################################
5 | # CONFIGURATION
6 | ############################################
7 |
8 | from hexiom_config import *
9 |
10 | ############################################
11 |
12 | ####
13 | # Helper functions
14 | ###
15 |
16 | def sign(n):
17 | if n < 0:
18 | return -1
19 | elif n == 0:
20 | return 0
21 | else: #n > 0
22 | return +1
23 |
24 | def enumerate1(xs):
25 | ''' (x1,x2...) -> ((1,x1), (2,x2),...) '''
26 | for (i,x) in enumerate(xs):
27 | yield (i+1, x)
28 |
29 | def concat(xss):
30 | ''' [[a]] -> [a] '''
31 | return [x for xs in xss for x in xs]
32 |
33 | def permutations(xs):
34 | def go(i):
35 | if i >= len(xs):
36 | yield []
37 | else:
38 | for x in xs[i]:
39 | for p in go(i+1):
40 | yield [x] + p
41 | return go(0)
42 |
43 | def assoc_list(xs):
44 | return list(enumerate(xs))
45 |
46 | def assoc_get(x, xs):
47 | for (k,v) in xs:
48 | if k == x:
49 | return v
50 | return None
51 |
52 | ##
53 | # Building rules
54 | #
55 |
56 | def size_of_dims(dims):
57 | s = 1
58 | for (minv, maxv) in dims:
59 | if minv <= maxv:
60 | s *= (maxv - minv + 1)
61 | else:
62 | s *= 0
63 | return s
64 |
65 | def dim_multipliers(dims):
66 | ''' mult[i] = size_of(dims[i+1:])'''
67 |
68 | multipliers = [None] * len(dims)
69 | multipliers[-1] = 1
70 |
71 | for i in range(len(dims)-2, -1, -1):
72 | (minv, maxv) = dims[i+1]
73 | multipliers[i] = \
74 | multipliers[i+1] * \
75 | (maxv - minv + 1)
76 |
77 | return multipliers
78 |
79 | class InvalidVariable(Exception):
80 | pass
81 |
82 |
83 | class RuleSet(object):
84 |
85 | def __init__(self):
86 | self.vsets = []
87 | self.vsets_by_name = {}
88 | self.next_free_variable = 1
89 |
90 | class Variable(object):
91 | def __init__(self, vset, vs):
92 | self.vset = vset
93 | self.vs = vs
94 |
95 | def __str__(self):
96 | return self.vset.name + '(' + ' '.join(map(str,self.vs)) + ')'
97 |
98 | def __eq__(self, b):
99 | return (self.vset is b.vset) and (self.vs == b.vs)
100 |
101 | def offsetOf(self):
102 | return self.toInt() - self.vset.first_variable
103 |
104 | def toInt(self):
105 | return self.vset.indexOfVariable(self)
106 |
107 | class VariableSet(object):
108 | def __init__(self, ruleset, name, dims):
109 | self.name = name
110 |
111 | self.ndims = len(dims)
112 | self.dims = dims
113 |
114 | self.size = size_of_dims(dims)
115 |
116 | self.multipliers = dim_multipliers(dims)
117 |
118 | self.ruleset = ruleset
119 | ruleset.vsets.append(self)
120 |
121 | if ruleset.vsets_by_name.get(name):
122 | raise Exception('repeated name %s'%name)
123 | ruleset.vsets_by_name[name] = self
124 |
125 | self.first_variable = self.ruleset.next_free_variable
126 | self.ruleset.next_free_variable += self.size
127 |
128 | def __call__(self, *vs):
129 | if self.ndims != len(vs):
130 | raise InvalidVariable(
131 | 'Expected %d dimensions, got %d'%(
132 | self.ndims, len(vs))
133 | )
134 |
135 | for (val, (min_val, max_val)) in zip(vs, self.dims):
136 | if not (min_val <= val <= max_val):
137 | raise InvalidVariable(
138 | 'Variable out of bounds:', self.name, vs, self.dims
139 | )
140 |
141 | return Lit(+1, RuleSet.Variable(self, vs) )
142 |
143 | def contains(self, n):
144 | return 0 <= (n-self.first_variable) < self.size
145 |
146 | def indexOfVariable(self, var):
147 | if var.vset is not self:
148 | raise Exception('Converting variable at the wrong place')
149 |
150 | return self.first_variable + sum([
151 | (val - minv)*mult
152 | for (val, (minv, maxv), mult) in
153 | zip(var.vs, self.dims, self.multipliers)
154 | ])
155 |
156 | def valuesOfIndex(self, n):
157 | offset = n - self.first_variable
158 |
159 | vs = []
160 | for mult in self.multipliers:
161 | vs.append(offset//mult)
162 | offset = offset%mult
163 |
164 | return tuple(vs)
165 |
166 | def VarSet(self, name, dims):
167 | return RuleSet.VariableSet(self, name, dims)
168 |
169 | def print_cnf_file(self, formulation, fil):
170 | ruleset = formulation.ruleset
171 | clauses = formulation.clauses
172 |
173 | with open(NAMED_CNF_INPUT_FILE, 'w') as dbg:
174 |
175 | print >> fil, 'p cnf', ruleset.next_free_variable -1 , len(clauses)
176 | for (i,clause) in enumerate(clauses):
177 | for lit in clause:
178 | print >> dbg, lit,
179 | print >> dbg
180 |
181 | intClause = [lit.sign*lit.var.toInt() for lit in clause]
182 | for lit in intClause:
183 | print >> fil, lit,
184 | print >> fil, '0'
185 |
186 | nvars = self.next_free_variable - 1
187 |
188 | def get_varset_by_name(self, name):
189 | return self.vsets_by_name.get(name)
190 |
191 | def get_varset_by_variable(self, n):
192 | beg = 0
193 | end = len(self.vsets)
194 | while(beg < end):
195 | m = (beg + end) // 2
196 | vset = self.vsets[m]
197 | if n < vset.first_variable:
198 | end = m
199 | elif n >= vset.first_variable + vset.size:
200 | beg = m+1
201 | else:
202 | return vset
203 | return None
204 |
205 | def cnfVarToLit(self, lit_n):
206 | sgn = (1 if lit_n >= 0 else -1)
207 | n = abs(lit_n)
208 | varset = self.get_varset_by_variable(n)
209 | return Lit(
210 | sgn,
211 | RuleSet.Variable(varset, varset.valuesOfIndex(n))
212 | )
213 | #####
214 | # Logical connectives
215 | ####
216 |
217 | class Logic(object):
218 | def __pos__(self):
219 | return self
220 |
221 | class Lit(Logic):
222 | def __init__(self, sign, var):
223 | self.sign = sign
224 | self.var = var
225 |
226 | def __str__(self):
227 | sgn = ('+' if self.sign > 0 else '-')
228 | return sgn + str(self.var)
229 |
230 | def __neg__(self):
231 | return Lit(-self.sign, self.var)
232 |
233 | def to_cnf(self):
234 | return [[self]]
235 |
236 | class And(Logic):
237 | def __init__(self, cs):
238 | self.cs = cs
239 |
240 | def __neg__(self):
241 | return Or([-c for c in self.cs])
242 |
243 | def to_cnf(self):
244 | return concat(c.to_cnf() for c in self.cs)
245 |
246 | class Or(Logic):
247 | def __init__(self, cs):
248 | self.cs = cs
249 |
250 | def __neg__(self):
251 | return And([-c for c in self.cs])
252 |
253 | def to_cnf(self):
254 | return map(concat, permutations( [c.to_cnf() for c in self.cs] ) )
255 |
256 | def Implies(a,b):
257 | return Or([-a, +b])
258 |
259 | def Equivalent(a,b):
260 | return And([
261 | Implies(a,b),
262 | Implies(b,a)
263 | ])
264 |
265 | def BruteForceOnlyOne(xs):
266 | rs = [Or(xs)]
267 | for (i,a) in enumerate(xs):
268 | for (j,b) in enumerate(xs):
269 | if (i != j):
270 | rs.append( Implies(a, -b) )
271 | return And(rs)
272 |
273 | def sumOfVariables(S, T, variables, maxk):
274 | # S(k, i) = there are at leat k truthy values
275 | # among the first i variables
276 | # T(k) = there are k truthy values
277 |
278 | n = len(variables)
279 |
280 | if(maxk is None): maxk = n
281 |
282 | rules = []
283 |
284 | ## S
285 |
286 | for i in range(0, n+1):
287 | rules.append( S(0, i) )
288 |
289 | for k in range(1, maxk+1):
290 | rules.append( -S(k, 0) )
291 | for i in range(1, n+1):
292 | rules.append(Equivalent(
293 | S(k, i),
294 | Or([
295 | S(k, i-1),
296 | And([ variables[i-1], S(k-1, i-1) ])
297 | ])
298 | ))
299 |
300 | ## T
301 | for k in range(0, maxk):
302 | rules.append(Equivalent(
303 | T(k), And([S(k, n), -S(k+1, n)])
304 | ))
305 |
306 | rules.append(Equivalent(
307 | T(maxk), S(maxk,n)
308 | ))
309 |
310 | return And(rules)
311 |
312 | def vectorLessThenOrEqual(E, xs, ys):
313 | # n = len(xs) = len(ys)
314 | # E_i, i in [0,n] := xs[0:i] == ys[0:i]
315 |
316 | n = len(xs)
317 |
318 | if(len(ys) != n):
319 | raise Exception('Imcompatible vector lengths')
320 |
321 | rules = []
322 |
323 | ## Eq
324 | rules.append( +E(0) )
325 | for (i, (x, y)) in enumerate1(zip(xs, ys)):
326 | rules.append(Equivalent(
327 | E(i),
328 | And([ E(i-1), Equivalent(x, y) ])
329 | ))
330 |
331 | ## x < y
332 | for i in range(0,n):
333 | rules.append(
334 | Implies( E(i), Implies(xs[i], ys[i]) )
335 | )
336 |
337 | return And(rules)
338 |
339 | ###########
340 | # Neighbours
341 | ###########
342 |
343 | # Radial Hexagonal coordinates
344 | # (r, c, d)
345 | # r = distance from center
346 | # c = vértice associado
347 | # d = indice no lado (0 é o vértice, r-1 é o último)
348 | #
349 | # |0__ |1
350 | # / \
351 | # 5 --
352 | # -- 2
353 | # \ /
354 | # 4| __3|
355 |
356 | #Directional Hexagonal coordinates
357 | # (a,b)
358 | # / a
359 | # - b
360 | #
361 | # (-1,-1) (-1, 0)
362 | # ( 0,-1) ( 0, 0) ( 0, 1)
363 | # ( 1, 0) (1, 1)
364 |
365 | # Positional coordinates, as they come
366 | # from the input:
367 | #
368 | # 0 1
369 | # 2 3 4
370 | # 5 6
371 |
372 | #Simmetry functions
373 |
374 | def reflect_0_5((r, c, d)):
375 | if r == 0 and c == 0 and d == 0:
376 | return (0,0,0)
377 | else:
378 | if d == 0:
379 | return (r, 5-c, 0)
380 | else:
381 | return (r, (10-c)%6, r-d)
382 |
383 | def clockwise_rotate(n, (r,c,d)):
384 | if r == 0 and c == 0 and d == 0:
385 | return (0,0,0)
386 | else:
387 | return (r, (c+n)%6, d)
388 |
389 | class HexTopology(object):
390 | def __init__(self, side):
391 | self.rcd_to_m = {}
392 | self.ab_to_m = {}
393 |
394 | self.side = side
395 |
396 | self.rcds = []
397 | self.abs = []
398 | self.ps = []
399 |
400 | m_ = [0]
401 | a_ = [None]
402 | b_ = [None]
403 | def match(rcd):
404 | m = m_[0]
405 | ab = (a_[0], b_[0])
406 |
407 | self.abs.append(ab)
408 | self.rcds.append(rcd)
409 | self.ps.append(m)
410 |
411 | self.rcd_to_m[rcd] = m
412 | self.ab_to_m[ab] = m
413 |
414 | m_[0] += 1
415 | b_[0] += 1
416 | #print ''.join(map(str,rcd)),
417 |
418 | radius = side-1
419 |
420 | #top half
421 | for r in range(radius, 0, -1):
422 | a_[0] = -r
423 | b_[0] = -radius
424 | #print ' '*r,
425 | for i in range(0, radius-r):
426 | match((radius-i, 5, r))
427 | for i in range(r):
428 | match((r, 0, i))
429 | for i in range(0, radius+1-r):
430 | match((r+i, 1, i))
431 | #print
432 |
433 | #middle divider
434 | a_[0] = 0
435 | b_[0] = -radius
436 | #print '',
437 | for r in range(radius, 0, -1):
438 | match((r, 5, 0))
439 | if(radius >= 0):
440 | match((0,0,0))
441 | for r in range(1, radius+1):
442 | match((r, 2, 0))
443 | #iprint
444 |
445 | #lower half
446 | for r in range(1, radius+1):
447 | a_[0] = r
448 | b_[0] = -(radius-r)
449 | #print ' '*r,
450 | for i in range(0, radius+1-r):
451 | match((radius-i, 4, radius-r-i))
452 | for i in range(r):
453 | match((r, 3, r-i-1))
454 | for i in range(0, radius-r):
455 | match((r+1+i, 2, r))
456 | #print
457 |
458 | def print_in_hex(self, xs):
459 | xs = list(reversed(xs))
460 | side = self.side
461 | lines = []
462 |
463 | def show(n):
464 | return ('.' if n is None else str(n))
465 |
466 | #upper half (with middle line)
467 | for (i,a) in enumerate(range(1-side, 0+1)):
468 | line = []
469 | for b in range(1-side, i+1):
470 | line.append(show(xs.pop()))
471 | lines.append( ' '*(side-i-1) + ' '.join(line) )
472 | #lower half (without middle line)
473 | for (i,a) in enumerate1(range(1, side)):
474 | line = []
475 | for b in range(1-side+i, side):
476 | line.append(show(xs.pop()))
477 | lines.append( ' '*i + ' '.join(line) )
478 | return '\n'.join(lines)
479 |
480 | def hex_adjacency_graph(self):
481 | adj_list = {}
482 |
483 | def add(m, n):
484 | adj_list[m].append(n)
485 |
486 | def is_adj(m,n):
487 | add(m, n)
488 | if not adj_list.has_key(n):
489 | adj_list[n] = []
490 | add(n, m)
491 |
492 | for h in self.abs:
493 | (a,b) = h
494 |
495 | if not adj_list.has_key(h):
496 | adj_list[h] = []
497 |
498 | for h_ in [
499 | (a-1, b-1), (a-1, b),
500 | (a, b+1)
501 | ]:
502 | if self.ab_to_m.get(h_) is not None:
503 | is_adj(h, h_)
504 |
505 | for lst in adj_list.values():
506 | lst.sort()
507 |
508 | return adj_list
509 |
510 | def pos_adjacency_graph(self):
511 | adj_list = {}
512 |
513 | for (k, vs) in self.hex_adjacency_graph().iteritems():
514 | adj_list[ self.ab_to_m[k] ] =\
515 | [ self.ab_to_m[v] for v in vs]
516 |
517 | return adj_list
518 |
519 | def simmetries(self):
520 | simmetries = []
521 |
522 | def add_sim(rcds):
523 | simmetries.append([
524 | self.rcd_to_m[rcd]
525 | for rcd in rcds
526 | ])
527 |
528 | for n in range(6):
529 | add_sim([
530 | clockwise_rotate(n, rcd)
531 | for rcd in self.rcds
532 | ])
533 |
534 | for n in range(6):
535 | add_sim([
536 | reflect_0_5(clockwise_rotate(n, rcd))
537 | for rcd in self.rcds
538 | ])
539 |
540 | return simmetries
541 |
542 | ###########
543 | # Input
544 | ###########
545 |
546 | import re
547 |
548 | class ProblemInput(object):
549 | def __init__(self, side, counts, blocked_positions, fixed_positions, lines):
550 | self.side = side
551 | self.counts = counts
552 | self.blocked_positions = blocked_positions
553 | self.fixed_positions = fixed_positions
554 | self.lines = lines
555 |
556 | def print_to_stdout(self):
557 | print self.side
558 | for line in self.lines:
559 | print line,
560 |
561 | def read_input(fil):
562 | side = int(fil.readline())
563 | counts = [0]*7
564 | blocked_positions = []
565 | fixed_positions = []
566 |
567 | lines = []
568 |
569 | m=0
570 | for line in fil:
571 | lines.append(line)
572 | for match in re.finditer(r'(\+?)(\.|\d)', line):
573 | locked = (match.group(1) == '+')
574 | n = (None if match.group(2) == '.' else int(match.group(2)))
575 |
576 | if n is not None:
577 | counts[n] += 1
578 |
579 | if locked:
580 | if n is None:
581 | blocked_positions.append(m)
582 | else:
583 | fixed_positions.append( (m, n) )
584 |
585 | m += 1
586 |
587 | return ProblemInput(
588 | side,
589 | assoc_list(counts),
590 | blocked_positions,
591 | fixed_positions,
592 | lines
593 | )
594 |
595 | ####
596 | # Create clauses
597 | ####
598 |
599 | class SATFormulation(object):
600 | def __init__(self, board_input, ruleset, topology, clauses):
601 | self.board_input = board_input
602 | self.ruleset = ruleset
603 | self.topology = topology
604 | self.clauses = clauses
605 |
606 | def SAT_formulation_from_board_input(board_input):
607 |
608 | ruleset = RuleSet()
609 | topology = HexTopology(board_input.side)
610 |
611 | # Schema
612 | ########
613 |
614 | slot_range = (0, topology.ps[-1])
615 | slot_count = len(topology.ps)
616 |
617 | value_range = (0, 6)
618 |
619 | # (m,n) = There is an n-valued tile at slot m
620 | # The m-th slot has n occupied neighbors
621 | Placement = ruleset.VarSet('P', [slot_range, value_range])
622 |
623 | # (m) = is the m-th tile occupied?
624 | Occupied = ruleset.VarSet('O', [slot_range])
625 |
626 | # (m)(k,i) = m-th slot has k occupied slots among its first i neighbours
627 | NeighbourPartialSum = []
628 |
629 | # (m)(k) = m-th slot has k occupied slots among its neighbours
630 | NeighbourSum = []
631 |
632 |
633 | # (n)(k,i) = there are k n-valued tiles on the first i slots?
634 | TilePartialSum = []
635 |
636 | # Rules
637 | #######
638 |
639 | print '== Creating CNF description of level'
640 |
641 | rules = []
642 |
643 | print 'Creating level-state rules...'
644 |
645 | for (m, n) in board_input.fixed_positions:
646 | rules.append( +Placement(m, n) )
647 |
648 | for m in board_input.blocked_positions:
649 | rules.append( -Occupied(m) )
650 |
651 | print 'Creating tile placement rules...'
652 |
653 | for m in topology.ps:
654 | rules.append( BruteForceOnlyOne(
655 | [-Occupied(m)] + [+Placement(m,n) for n in range(7)]
656 | ))
657 |
658 | adj_graph = topology.pos_adjacency_graph()
659 |
660 | print 'Constraining number of neighbour of occupied tiles...'
661 |
662 | for m in topology.ps:
663 | vs = adj_graph[m]
664 | nvs = len(vs)
665 |
666 | NPSum = ruleset.VarSet('Nps_'+str(m),[
667 | (0, nvs), (0, nvs) ])
668 | NeighbourPartialSum.append(NPSum)
669 |
670 | NSum = ruleset.VarSet('Ns_'+str(m), [
671 | (0, nvs) ])
672 | NeighbourSum.append(NSum)
673 |
674 | rules.append(sumOfVariables(
675 | NPSum, NSum,
676 | [+Occupied(v) for v in adj_graph[m]],
677 | None
678 | ))
679 |
680 | for n in range(0, nvs+1):
681 | rules.append(Implies(
682 | Occupied(m),
683 | Equivalent( +Placement(m,n), +NSum(n) )
684 | ))
685 |
686 | for n in range(nvs+1, 7):
687 | rules.append( -Placement(m,n) )
688 |
689 | print 'Creating constraints for the amount of tiles used...'
690 |
691 | empty_count = len(topology.ps) - sum([c for (_,c) in board_input.counts])
692 |
693 | # (k,i) = k empty slots among the first i slots
694 | EmptyPartialSum = ruleset.VarSet('Eps', [
695 | (0, empty_count+1), (0, slot_count) ])
696 |
697 | EmptySum = ruleset.VarSet('Es', [
698 | (0, empty_count+1)
699 | ])
700 |
701 | rules.append(sumOfVariables(
702 | EmptyPartialSum, EmptySum,
703 | [ -Occupied(m) for m in topology.ps ],
704 | empty_count + 1
705 | ))
706 |
707 | for n in range(0, 7):
708 | tile_count = assoc_get(n, board_input.counts)
709 |
710 | TPSum = ruleset.VarSet('Tps_'+str(n),[
711 | (0, tile_count+1), (0, slot_count) ])
712 | TilePartialSum.append(TPSum)
713 |
714 | TSum = ruleset.VarSet('Ts_'+str(n), [
715 | (0, tile_count+1) ])
716 |
717 | rules.append(sumOfVariables(
718 | TPSum, TSum,
719 | [ Placement(m,n) for m in topology.ps ],
720 | tile_count + 1
721 | ))
722 | rules.append( +TSum(tile_count) )
723 |
724 | print 'Adding simmetry-breaking rules...'
725 |
726 | def simmetry_is_preserved(xs, ys):
727 | xys = zip(xs, ys)
728 |
729 | for m in board_input.blocked_positions:
730 | m_ = assoc_get(m, xys)
731 | if m_ not in board_input.blocked_positions:
732 | #print m, 'to', m_, 'simmetry not found'
733 | return False
734 |
735 | for (m,v) in board_input.fixed_positions:
736 | m_ = assoc_get(m, xys)
737 | if (m_,v) not in board_input.fixed_positions:
738 | #print (m,v), 'to', (m_, v), 'simmetry not found'
739 | return False
740 | return True
741 |
742 | def vars_from_sim(ms):
743 | vs = []
744 | for m in ms:
745 | vs.append( +Occupied(m) )
746 | vs.extend([ +Placement(m,n) for n in range(0, 7) ])
747 | return vs
748 |
749 | simms = topology.simmetries()
750 | sim0 = simms[0]
751 | vsim0 = vars_from_sim(sim0)
752 | for (i,sim1) in enumerate1( simms[1:] ):
753 | if simmetry_is_preserved(sim0, sim1):
754 | print ' (Simmetry #%s found!)'%(i)
755 | vsim1 = vars_from_sim(sim0)
756 | rules.append(vectorLessThenOrEqual(
757 | ruleset.VarSet('SimEq_'+str(i), [(0, len(vsim0))]),
758 | vsim0,
759 | vsim1
760 | ))
761 |
762 | print 'Converting rules to CNF form...'
763 |
764 | return SATFormulation(
765 | board_input,
766 | ruleset,
767 | topology,
768 | And(rules).to_cnf()
769 | )
770 |
771 |
772 | def get_SAT_assignments(fil):
773 | assignments = []
774 | for line in fil:
775 | if 'UNSAT' in line.upper():
776 | return None
777 | for word in line.split():
778 | if re.match(r'-?\d+$', word):
779 | n = int(word)
780 | if n == 0:
781 | return assignments
782 | else:
783 | assignments.append(n)
784 | return assignments
785 |
786 |
787 | def print_board_from_assignments(formulation, assignments):
788 |
789 | ruleset = formulation.ruleset
790 | topology = formulation.topology
791 |
792 | P = ruleset.get_varset_by_name('P')
793 |
794 | layout = [None for p in topology.ps]
795 | with open(NAMED_CNF_RESULT_FILE, 'w') as result:
796 | for lit in assignments:
797 | sgn = sign(lit)
798 | var = abs(lit)
799 |
800 | print >> result, ruleset.cnfVarToLit(lit)
801 |
802 | if sgn > 0 and P.contains(var):
803 | (m,n) = P.valuesOfIndex(var)
804 | layout[m] = n
805 |
806 | print '=== Initial input: ==='
807 | formulation.board_input.print_to_stdout()
808 |
809 | print '=== Solution ==='
810 | print
811 | print topology.print_in_hex(layout)
812 |
813 | ########
814 | # Main
815 | ########
816 |
817 | import sys
818 |
819 | def main():
820 |
821 | if len(sys.argv) <= 1:
822 | print "usage: ./hexiom_solve.py [0-40]"
823 | exit(1)
824 |
825 | level_no = int(sys.argv[1])
826 |
827 | input_filename = LEVEL_INPUT_FILENAME_PATTERN(level_no)
828 | cnf_in_filename = SAT_INPUT_FILENAME_PATTERN(level_no)
829 | cnf_out_filename = SAT_OUTPUT_FILENAME_PATTERN(level_no)
830 |
831 | with open(input_filename, 'r') as fil:
832 | board_input = read_input(fil)
833 |
834 | print '== Level to solve== '
835 | board_input.print_to_stdout()
836 |
837 | formulation = SAT_formulation_from_board_input(board_input)
838 |
839 | print '=== Writing CNF to file ==='
840 | with open(cnf_in_filename, 'w') as fil:
841 | formulation.ruleset.print_cnf_file(formulation, fil )
842 | print '=== Done! Calling SAT solver now ==='
843 |
844 | SAT_SOLVE(cnf_in_filename, cnf_out_filename)
845 |
846 | with open(cnf_out_filename, 'r') as fil:
847 | assignments = get_SAT_assignments(fil)
848 |
849 | if assignments is None:
850 | print '*** Got UNSAT result! ***'
851 | else:
852 | print '** Solution found! ***'
853 | print_board_from_assignments(formulation, assignments)
854 |
855 | if __name__ == '__main__':
856 | main()
857 |
--------------------------------------------------------------------------------
/levels/level00.txt:
--------------------------------------------------------------------------------
1 | 1
2 | .
3 |
--------------------------------------------------------------------------------
/levels/level01.txt:
--------------------------------------------------------------------------------
1 | 2
2 | . .
3 | 1 . 1
4 | . .
5 |
--------------------------------------------------------------------------------
/levels/level02.txt:
--------------------------------------------------------------------------------
1 | 2
2 | . 1
3 | . 1 1
4 | 1 .
5 |
--------------------------------------------------------------------------------
/levels/level03.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 2 . 2
3 | . 2 2 .
4 | 2 2 . 2 2
5 | . 2 2 .
6 | 2 . 2
7 |
--------------------------------------------------------------------------------
/levels/level04.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 3 . 3
3 | . . . .
4 | 3 .+6 . 3
5 | . . . .
6 | 3 . 3
7 |
--------------------------------------------------------------------------------
/levels/level05.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . .+1
3 | 2 . . .
4 | . . 1 . .
5 | . . . 2
6 | +2 . .
7 |
--------------------------------------------------------------------------------
/levels/level06.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 2 . 2
3 | 2 . . .
4 | . .+. . 2
5 | +1+. . 2
6 | +.+1 .
7 |
--------------------------------------------------------------------------------
/levels/level07.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . 1 2
3 | . .+0 1
4 | . . . . .
5 | 1+0 . .
6 | 2 1 .
7 |
--------------------------------------------------------------------------------
/levels/level08.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 4 1 4
3 | 1 . . 1
4 | 4 . 6 . 4
5 | 1 . . 1
6 | 4 1 4
7 |
--------------------------------------------------------------------------------
/levels/level09.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 3 1 3
3 | +1+.+. 4
4 | .+. .+. 3
5 | 2 .+.+1
6 | . 2 .
7 |
--------------------------------------------------------------------------------
/levels/level10.txt:
--------------------------------------------------------------------------------
1 | 3
2 | +.+. .
3 | +. 0 . 2
4 | . 1+2 1 .
5 | 2 . 0+.
6 | .+.+.
7 |
--------------------------------------------------------------------------------
/levels/level11.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 2 .+2
3 | . 1+. .
4 | 2 1+. 1 2
5 | .+. 1 .
6 | +2 . 2
7 |
--------------------------------------------------------------------------------
/levels/level12.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . . .
3 | . . . .
4 | 3 . 3 . .
5 | 1 1 1 3
6 | 4 3 1
7 |
--------------------------------------------------------------------------------
/levels/level13.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 3 2 .
3 | 4 . . 2
4 | 3 . . . 3
5 | 2 . . 4
6 | . 2 3
7 |
--------------------------------------------------------------------------------
/levels/level14.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . 3 .
3 | . 4 . .
4 | . . 3 . 2
5 | 3 . . .
6 | . 2 3
7 |
--------------------------------------------------------------------------------
/levels/level15.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 2 4 .
3 | . 2 . 2
4 | 2 2 2 . 3
5 | . . . .
6 | 3 3 3
7 |
--------------------------------------------------------------------------------
/levels/level16.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 4 . 2
3 | . 0 3 .
4 | . 0 5 4 .
5 | 4 3 3 .
6 | 3 2 5
7 |
--------------------------------------------------------------------------------
/levels/level17.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 2 . 1
3 | 2+. 3 .
4 | 2+. .+. 2
5 | . 3+. 2
6 | 1 . 2
7 |
--------------------------------------------------------------------------------
/levels/level18.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 3 5 4
3 | 4 . 3 2
4 | +3 4+. 3+3
5 | . 1+. .
6 | 3 2+.
7 |
--------------------------------------------------------------------------------
/levels/level19.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . 2 3
3 | . 2 2 2
4 | . 1 3 2 .
5 | 2 2 3 .
6 | 3 1 .
7 |
--------------------------------------------------------------------------------
/levels/level20.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . 5 4
3 | . 2+.+1
4 | . 3+2 3 .
5 | +2+. 5 .
6 | . 3 .
7 |
--------------------------------------------------------------------------------
/levels/level21.txt:
--------------------------------------------------------------------------------
1 | 3
2 | . 2 .
3 | 1 2 2 .
4 | 2 2 2 2 .
5 | 1 2 2 .
6 | 3 3 .
7 |
--------------------------------------------------------------------------------
/levels/level22.txt:
--------------------------------------------------------------------------------
1 | 4
2 | . . 2 3
3 | . 2 4 3 2
4 | . 2 2 . 3 .
5 | . 2 2 3 2 2 .
6 | . 3 . 2 2 .
7 | 2 2 4 2 .
8 | 3 2 . .
9 |
10 |
--------------------------------------------------------------------------------
/levels/level23.txt:
--------------------------------------------------------------------------------
1 | 4
2 | . . . 1
3 | 1+.+.+. 1
4 | 1+. . .+. 2
5 | 1+. 0 . .+. 1
6 | .+. . .+. 1
7 | 2+.+.+. 1
8 | 2 1 1 2
9 |
10 |
--------------------------------------------------------------------------------
/levels/level24.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 4 2 3
3 | 5 4 . 2
4 | 3 . 4 4 4
5 | . . . 5
6 | 3 . 3
7 |
--------------------------------------------------------------------------------
/levels/level25.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 4 . .
3 | . . 2 .
4 | 4 3 2 . 4
5 | 2 2 3 .
6 | 4 2 4
7 |
--------------------------------------------------------------------------------
/levels/level26.txt:
--------------------------------------------------------------------------------
1 | 3
2 | +. . 3
3 | 2 2 1 4
4 | 1 2 . . .
5 | 2 4 3 4
6 | 4 4+.
7 |
--------------------------------------------------------------------------------
/levels/level27.txt:
--------------------------------------------------------------------------------
1 | 3
2 | +3 2 6
3 | . .+4 3
4 | 5 4 1 4 6
5 | . 3+3 .
6 | 3 1 .
7 |
--------------------------------------------------------------------------------
/levels/level28.txt:
--------------------------------------------------------------------------------
1 | 4
2 | 2 2 3 4
3 | 2 2 5 2 2
4 | 3 5 . . 2 4
5 | 4 2 . . .+. 3
6 | 2 2 . .+. 3
7 | 4+.+.+. .
8 | 3 3 . .
9 |
10 |
--------------------------------------------------------------------------------
/levels/level29.txt:
--------------------------------------------------------------------------------
1 | 4
2 | . 4 6 .
3 | 4 4 2 . 4
4 | . 4 . 2 4 .
5 | . . . . . . 4
6 | 6 4 . 4 4 .
7 | 4 . 2 . 4
8 | . 2 . 4
9 |
10 |
--------------------------------------------------------------------------------
/levels/level30.txt:
--------------------------------------------------------------------------------
1 | 4
2 | 5 5 . .
3 | 3 . 2+2 6
4 | 3 . 2 . 5 .
5 | . 3 3+4 4 . 3
6 | 4 5 4 . 5 4
7 | 5+2 . . 3
8 | 4 . . .
9 |
10 |
--------------------------------------------------------------------------------
/levels/level31.txt:
--------------------------------------------------------------------------------
1 | 3
2 | 5 3 2
3 | . . 3 2
4 | 3 3 . . .
5 | 3 4 3 5
6 | 3 5 4
7 |
--------------------------------------------------------------------------------
/levels/level32.txt:
--------------------------------------------------------------------------------
1 | 4
2 | +2 . 4+.
3 | . 4 4 . 3
4 | . 3 3 4 . .
5 | +2 3 3+. 5 3+2
6 | 2 . 4 4 4 3
7 | 3 . . . 4
8 | +. 3 .+2
9 |
10 |
--------------------------------------------------------------------------------
/levels/level33.txt:
--------------------------------------------------------------------------------
1 | 4
2 | +2 . 2 .
3 | 3+5 . . 4
4 | 3 2+4 3 4 4
5 | 6 4 3+4 . . 3
6 | 1 4 3+5 2 4
7 | . . .+5 3
8 | . 2 .+3
9 |
10 |
--------------------------------------------------------------------------------
/levels/level34.txt:
--------------------------------------------------------------------------------
1 | 4
2 | 4 2 4 2
3 | . . .+. 3
4 | 2+. .+. . 2
5 | 4 2+. .+. 2 4
6 | . 2+. .+. 2
7 | 4+. 3 2 .
8 | 2 . 2 4
9 |
10 |
--------------------------------------------------------------------------------
/levels/level35.txt:
--------------------------------------------------------------------------------
1 | 4
2 | . 3 . .
3 | . 6 3 2 6
4 | 6 3 4 . 4 4
5 | . 6 . . . . .
6 | 4 2 3 . 3 .
7 | 3 . . . .
8 | 4 4 . .
9 |
10 |
--------------------------------------------------------------------------------
/levels/level36.txt:
--------------------------------------------------------------------------------
1 | 4
2 | 2 1 1 2
3 | 3 3 3 . .
4 | 2 3 3 . 4 .
5 | . 2 . 2 4 3 2
6 | 2 2 . . . 2
7 | 4 3 4 . .
8 | 3 2 3 3
9 |
10 |
--------------------------------------------------------------------------------
/levels/level37.txt:
--------------------------------------------------------------------------------
1 | 4
2 | 1 4 . 1
3 | 4 . 6 2 5
4 | . 5 . 2 . 4
5 | . 2 2 4+4 . .
6 | 1 2+5 3 3 3
7 | 5 2 . 4 .
8 | 4 3 . 3
9 |
10 |
--------------------------------------------------------------------------------
/levels/level38.txt:
--------------------------------------------------------------------------------
1 | 5
2 | +3 4 3 3+3
3 | 4 4 3 2 4 5
4 | . 3 3 2 4 4 3
5 | 2 . 2 . . . . 2
6 | +3 4 . . 4 3 5 4+3
7 | 5 4 4 4 4 2 4 3
8 | 3 3 . . . . 3
9 | 5 3 5 4 . 4
10 | +3 5 4 4+3
11 |
12 |
--------------------------------------------------------------------------------
/levels/level39.txt:
--------------------------------------------------------------------------------
1 | 5
2 | 6 4+2 3 .
3 | . 5 6+. 4 5
4 | +2 . 2 2+. 3 3
5 | .+. . 3 . . 2 4
6 | 4 4+. 4 4 3+. 4 .
7 | . 4 2 3 2 .+. .
8 | 4 2+. . . .+2
9 | 3 2+. 4 2 3
10 | 4 3+2 3 4
11 |
12 |
--------------------------------------------------------------------------------
/levels/level40.txt:
--------------------------------------------------------------------------------
1 | 6
2 | 4 3 5 3 5 .
3 | 4 4 2 4 5 . .
4 | 4 4 4 2 . 4 . 5
5 | 4 5 5 4 5 . 6 5 .
6 | 3 . 5 3 4 4 . 3 6 3
7 | . . 5 3 3 . 4 5 4 4 4
8 | 5 6 4 4 4 4 . 6 5 5
9 | 3 . 6 5 2 4 4 3 3
10 | 5 5 5 . 5 4 5 5
11 | 2 . 5 3 3 4 3
12 | 6 4 4 4 4 4
13 |
14 |
--------------------------------------------------------------------------------
/lingeling:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomg/hexiom/be312775d6328b77c4b6a45c1556bf022dbef4a3/lingeling
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Using an industrial-strength SAT solver to solve the Hexiom puzzle
2 | ==================================================================
3 |
4 | Recently there was some talk on Reddit
5 | ([here](http://www.reddit.com/r/programming/comments/p54v2/solving_hexiom_perhaps_you_can_help/) and
6 | [here](http://www.reddit.com/r/programming/comments/pjx99/solving_hexiom_using_constraints/)) about using custom algorithms to play
7 | [Hexiom](http://www.kongregate.com/games/Moonkey/hexiom), a cute little Flash puzzle game from Kongregate.
8 |
9 | The original submitter has published some solutions using traditional backtracking and exaustive search on [his repository](https://github.com/slowfrog/hexiom), but I wandered
10 | if I could do better then him by leveraging a Boolean Satisfability solver. In particular, one of the problem sets (level 38) would take hours to complete and I wanted to see if I could do better then that.
11 |
12 | Why bother with a SAT solver?
13 | -----------------------------
14 |
15 | Why should I complicate things with a SAT solver instead of tinkering with an existing backtracking solution? My motivations are basically that
16 |
17 | * Modern SAT solvers have very optimized and smart brute forcing engines. They can do many tricks that most people do not know about or do not go through the trouble of implementing most of the time:
18 | * Non chronological backtracking (can go back multiple levels at once)
19 | * Efficient data structures / watched literals (backtracking is O(1))
20 | * Clause learning (avoids searching the same space multiple times)
21 | * Random and aggressive restarts (avoids staying too long in a dead end)
22 | * ...and much more!
23 |
24 | * SAT solver problem input is declarative so it is easy to add new rules and solving strategies without needing to rewrite the tricky backtracking innards.
25 | * This will be particularly important when we get to the symmetry-breaking optimizations.
26 |
27 | Or in other words, SAT solvers are very fast and let me easily do things I would usually not try to do using a more traditional approach.
28 |
29 | My final results
30 | ----------------
31 |
32 | In terms of speed, I **managed to solve the case that used to take hours in took hours in just over one minute**, while also still taking just a couple of seconds for the other easy problems.
33 |
34 | In terms of programming and algorithms, the good part is precisely that **I didn't have to do anything very special**.
35 | Besides encoding the problem using SAT, **the underlying algorithm is still exaustive backtracking, without any Hexiom-specific heuristics added1.**
36 |
37 | 1 (Well, one might try to count the symmetry-breaking as Hexiom-specific but the overall techinique is still pretty general...)
38 |
39 | -----------------------------
40 |
41 | How this all works
42 | ==================
43 |
44 | The puzzle
45 | ----------
46 |
47 | A Hexiom puzzle instance consists of an hexagonal playing field and a set of numbered, hexagonal, tiles. The objective consists in placing the tiles in slots on the board in such a way that the number on each tile corresponds to the amount of neighboring tiles it has.
48 |
49 | For example, level 8 presents the following initial placement of tiles (the dots represent empty board slots):
50 |
51 | 4 1 4
52 | 1 . . 1
53 | 4 . 6 . 4
54 | 1 . . 1
55 | 4 1 4
56 |
57 | And has the following solution
58 |
59 | 1 . 1
60 | . 4 4 .
61 | 1 4 6 4 1
62 | . 4 4 .
63 | 1 . 1
64 |
65 | Note how each **1** is surrounded by one other number, how each **4** is surrounded by four other numbers and how the **6** has a full set of 6 neighboring tiles around it.
66 |
67 | The Boolean Satisfiability Problem
68 | ----------------------------------
69 |
70 | SAT solvers, as the name indicates, solve the [boolean satisfiability](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem) problem. This problem consists of determining, given a set of boolean variables and a set of propositional predicates over these variables, whether there is a true-false assignment of the variables that satisfies all the predicates.
71 |
72 | For example, given variables `x`, `y`, and predicates
73 |
74 | 1) (NOT x) OR y
75 | 2) y OR z
76 | 3) (NOT y) OR (NOT z)
77 | 4) x
78 |
79 | We can produce the assignment {X=1, Y=1, Z=0} that satisfies all 4 clauses.
80 |
81 | However if where to add the 5-th clause
82 |
83 | 5) (NOT x) OR z
84 |
85 | then there would be no solutions.
86 |
87 | Modeling Hexiom using as a SAT instance.
88 | ----------------------------------------
89 |
90 | I used the following variable encodings to model the problem domain:
91 |
92 | * Om := The m-th slot of the board has a tile on it.
93 | * Pm,n := There is a n-valued tile on the m-th slot on the board
94 | * Nm,k := The m-th slot has k tiles neighboring it.
95 | * Tn,k := There are k n-valued tiles placed on the board.
96 | * ... and other helper variables for the cardinality constraints, etc.
97 |
98 | From this on its a matter of writing the predicates:
99 |
100 | * General predicates to define the variables as we intend them to be. They are mostly shared by all Hexiom instances of the same hegagon side length:
101 | * A cardinality constraint to say that a board slot is either empty or has a single slot on it
102 | * Cardinality constraints to define Nm,k
103 | * Cardinality constraints to define Tn,k
104 |
105 | * Level-dependant predicates, to describe the specific Hexiom level:
106 | * Set Tn,k according to the actual number ot tiles available.
107 | * Set Pm,n and Om for slots that come with preset values that cannot be changed.
108 |
109 | The only hard bit up to here is the cardinality constraints. For the small case (the rule for only a tile per slot) I " brute-forced" (O(n^2)) it and made a rule for each pair of variables saying at least one of them must be false.
110 |
111 | For the other cardinality constraints, I used an unary encoding, with helper variables such as `Nps(m, k, i) := There are at least k occupied tiles among the first i neighbors of tile m`. This gives a compact encoding, unlike the naïve version that lists all exponentialy-many possibilities.
112 |
113 | First results
114 | -------------
115 |
116 | With the initial description of the problem I already was already able to achieve results similar to those from the manually written backtracking search: all levels could be solved in under a couple of seconds, except for level 38, which took around half an hour to solve.
117 |
118 | Breaking symmetries
119 | -------------------
120 |
121 | The Hexiom instance that took the longest to solve was highly symmetrical so I suspected that the solver (and the backtracking-based approach) were wasting many cycles trying the same things multiple times (but in mirrored ways). I added *symmetry-breaking predicates* to rule out equivalent solutions from the search space.
122 |
123 | Hexagonal symmetries can be boiled down to the following 12 rotations and reflections (the 12 comes from 6 possible rotations times 2 for either doing a reflection or not):
124 |
125 |
126 | Rotations
127 |
128 | 1 2 6 1 5 6
129 | 6 0 3 5 0 2 4 0 1 ... and 3 more ...
130 | 5 4 4 3 4 2
131 |
132 | Reflections
133 |
134 | 1 6 6 5 5 4
135 | 2 0 5 1 0 4 6 0 3 ... and 3 more ...
136 | 3 4 2 3 1 2
137 |
138 | The trick behind writing a symmetry-breaking predicate is that if we arrange the variables corresponding to a solution in one of the permutations
139 |
140 | VX = O(1), P(1, 1..6), O(2), P(2, 1..6), ..., O(6), P(6, 1..6)
141 |
142 | And the corresponding variables after a symmetric transformation (say a clockwise rotation of 1/6th of a turn)
143 |
144 | VY = O(2), P(2, 1..6), O(3), P(3, 1..6), ..., O(1), P(1, 1..6)
145 |
146 | It is clear that given a satisfying assignment in VX we can find a symmetric assignment via VY. By imposing an arbitrary total order on these assignments we can force it so that only one of them is allowed (saving the SAT solver from exploring its reflections multiple times). The standard way to do this is to think of VX and VY as bit vectors representing integers and then write predicates that state the equivalent of `VX <= VY`.
147 |
148 | ----------------------
149 |
150 | How do I run this thing then?
151 | =============================
152 |
153 | python hexiom_solve.py NN
154 |
155 | Where NN is a number from 0 to 40 standing for the number of the level you want to solve. It will use an input file from the levels folder I copied from Slowfrog's project.
156 |
157 | Where do I get a SAT solver?
158 | =============================
159 |
160 | I am including copies of some SAT solver executables in this repo but I am not sure they will work on other computers and platforms.
161 |
162 | In any case, the hexiom solver was designed to be able to handle any SAT solver that uses the relatively standard DIMACS input and output formats. You can find a good selection of state of the art solvers in the websites of the annual [SAT Competition](http://satcompetition.org/).
163 |
164 | [Page of the SAT competition with links to the solver websites](http://www.cril.univ-artois.fr/SAT11/)
165 |
166 | Here are some of the best preforming SAT solvers from last year if you want to check them out:
167 |
168 | * [Glucose](http://www.lri.fr/~simon/?page=glucose) - [Source link](http://www.lri.fr/~simon/downloads/glucose-2-compet.tgz)
169 | * [Cryptominisat](http://www.msoos.org/cryptominisat2/) - [Source Link](https://gforge.inria.fr/frs/download.php/30138/cryptominisat-2.9.2.tar.gz)
170 | * [Lingeling](http://fmv.jku.at/lingeling/) - [Source Link](http://fmv.jku.at/lingeling/lingeling-587f-4882048-110513.tar.gz)
171 |
172 | As far as compiling goes, all the solvers I linked to are written in C or C++ and all of them come with an easy to use makefile or build script.
173 |
--------------------------------------------------------------------------------
/sat_in/.placeholder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomg/hexiom/be312775d6328b77c4b6a45c1556bf022dbef4a3/sat_in/.placeholder
--------------------------------------------------------------------------------
/sat_out/.placeholder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomg/hexiom/be312775d6328b77c4b6a45c1556bf022dbef4a3/sat_out/.placeholder
--------------------------------------------------------------------------------
/scratch/.placeholder:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomg/hexiom/be312775d6328b77c4b6a45c1556bf022dbef4a3/scratch/.placeholder
--------------------------------------------------------------------------------