|
import sys |
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_tile((a,b)): |
|
""" |
|
'Normalizes' a square-pair, so that tiles can be correctly compared |
|
to one another, even if ordered differently. |
|
|
|
For example, (4,7) is the same square-pair as (7,4). This function |
|
will return (4,7) for both of them. |
|
""" |
|
|
|
a,b = (b,a) if a < b else (a,b) |
|
|
|
return a,b |
|
|
|
|
|
def verify_dominosa_solution(n, |
|
square_placements, |
|
htile_placements, |
|
vtile_placements): |
|
""" |
|
Verifies that a dominosa solution is correct. |
|
|
|
n |
|
The maximum square-value in the game. |
|
square_placements |
|
A dictionary of the form { (col,row) => square-value }. Every |
|
valid pair of coordinates on the board must be represented. |
|
htile_placements |
|
See vtile_placements. |
|
vtile_placements |
|
A set of coordinate-tuples representing the lower or left |
|
position of each tile of the solution. For htile_placements, |
|
it is the left-most position of the tile, for vtile_placements, |
|
it is the lowest position of the tile. |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
# Make sure we discharge all of the possible square-pairs |
|
tile_set = set() |
|
|
|
# Keep track of each discharged tile |
|
tiled_pairs = {} |
|
|
|
# Make sure we cover the entire gameboard |
|
covered_placements = {} |
|
|
|
# Fill the initial tile-set with all possible square-pairs |
|
for i in range(n+1): |
|
for j in range(n+2): |
|
tile_set.add( normalize_tile( (i,j) ) ) |
|
|
|
|
|
|
|
def check_tile(orientation,(dcol,drow),(col,row)): |
|
if (col+0,row+0) not in square_placements \ |
|
or (col+dcol,row+drow) not in square_placements: |
|
reason = "Gameboard is missing a placement used in a tile." |
|
|
|
reason += " ({orientation} tile at {col},{row})." |
|
reason = reason.format(orientation=orientation,col=col,row=row) |
|
|
|
return False,reason |
|
|
|
if (col+0,row+0) in covered_placements \ |
|
or (col+dcol,row+drow) in covered_placements: |
|
reason = "Overlapping tiles." |
|
|
|
previous_tiles = set() |
|
|
|
if (col+0,row+0) in covered_placements: |
|
previous_tiles.add( covered_placements[(col+0,row)] ) |
|
if (col+dcol,row+drow) in covered_placements: |
|
previous_tiles.add( covered_placements[(col+1,row)] ) |
|
|
|
reason += " ({orientation} tile at {col},{row})." |
|
reason = reason.format(orientation=orientation,col=col,row=row) |
|
|
|
for previous_tile in previous_tiles: |
|
prev_dir,(prev_col,prev_row) = previous_tile |
|
reason += " (overlapping previous {orientation} tile at {col},{row})." |
|
reason = reason.format(orientation=prev_dir,col=prev_col,row=prev_row) |
|
|
|
|
|
return False,reason |
|
|
|
# remember that these board locations are taken (plus, remember |
|
# which tile took it, for debugging). |
|
covered_placements[ (col+0,row+0) ] = orientation,(col,row) |
|
covered_placements[ (col+dcol,row+drow) ] = orientation,(col,row) |
|
|
|
a = square_placements[(col+0,row+0)] |
|
b = square_placements[(col+dcol,row+drow)] |
|
|
|
tile_pair = normalize_tile((a,b)) |
|
|
|
if tile_pair not in tile_set: |
|
assert tile_pair in tiled_pairs |
|
|
|
reason = "Pair of squares tiled twice." |
|
|
|
reason += " (squares: {0}, {1})".format(a,b) |
|
|
|
reason += " ({orientation} tile at {col},{row})." |
|
reason = reason.format(orientation=orientation,col=col,row=row) |
|
|
|
|
|
prev_dir,(prev_col,prev_row) = tiled_pairs[ tile_pair ] |
|
reason += " (previous {orientation} tile at {col},{row})." |
|
reason = reason.format(orientation=prev_dir,col=prev_col,row=prev_row) |
|
|
|
|
|
return False, reason |
|
|
|
assert tile_pair not in tiled_pairs |
|
|
|
tile_set.discard(tile_pair) |
|
tiled_pairs[tile_pair] = orientation,(col,row) |
|
|
|
|
|
for (col,row) in htile_placements: |
|
orientation = "horizontal" |
|
dcol = +1 |
|
drow = 0 |
|
|
|
check_tile(orientation,(dcol,drow),(col,row)) |
|
for (col,row) in vtile_placements: |
|
orientation = "vertical" |
|
dcol = 0 |
|
drow = +1 |
|
|
|
check_tile(orientation,(dcol,drow),(col,row)) |
|
|
|
|
|
if len(tile_set) > 0: |
|
reason = "Tile-set is not discharged." |
|
|
|
reason += " tile-set: " + str(tile_set) |
|
|
|
return False,reason |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
def dominosa_from_1_in_3_sat(clauses,witness=None,increment_extra_n=False): |
|
""" |
|
clauses |
|
A list of 1-in-3 clauses of the form (t1,t2,t3), where |
|
t1,t2,t3 are each non-zero integers, representing variables in |
|
the formula; if ti is negative, it represents the negation of |
|
the variable in the clause. |
|
witness |
|
An optional set representing the solution to the formula. If |
|
present, then a witness-solution to the game will be output. |
|
The witness should be of the form set([t1,t2,t3 ...]), where ti |
|
is a non-zero integer representing a value in the formula; if ti |
|
is negative, it represents the negation of that variable. |
|
increment_extra_n |
|
This increments $n$ at the end of the formulation. This is |
|
useful to demonstrate how the top of the gameboard is tiled, as |
|
the type of tiling depends on the parity (even/odd-ness) of $n$. |
|
|
|
""" |
|
|
|
|
|
|
|
# coords from lower-left corner |
|
|
|
# make the clauses integer-index-able |
|
clauses = list(clauses) |
|
|
|
# list of veriables, to be populated next |
|
x = set() |
|
|
|
# variable to clause-indices, in the form {x_j => {clause-index}} |
|
x2ci = {} |
|
|
|
for ci,clause in enumerate(clauses): |
|
|
|
assert len(clause) == 3 |
|
|
|
for term in clause: |
|
x_j = abs(term) |
|
x.add(x_j) |
|
|
|
if x_j not in x2ci: |
|
x2ci[x_j] = set() |
|
x2ci[x_j].add(ci) |
|
|
|
|
|
|
|
# set of tiles that must be tiled somewhere in the solution; will |
|
# be populated as $n$ is incremented. See increment_n(), further |
|
# down. All of these should be "discharged" as we know that they |
|
# are placeable; see discharge_tile() further down. |
|
tile_set = set([(0,0)]) |
|
# keeps an index of {square-value => square-value} for all |
|
# tile-pairs in tile_set |
|
tile_set_index = { 0: set([0]) } |
|
# these are pairs that we want to force to be together; ie. we will |
|
# make force-gadgets for these, or find some other way to force-pair |
|
# them. See add_forcing_pair() and discharge_forcing_pair(), further |
|
# down. |
|
forcing_pairs = set() |
|
forcing_pairs_index = {} |
|
|
|
# this contains the board. It is of the form |
|
# { (col,row) => square-value } |
|
square_placements = {} |
|
# this contains names of certain special square-values, that can |
|
# optionally be used for a more semantic display |
|
square_names = {} |
|
|
|
# this is the set of builder values. if more than one builder value |
|
# is used, it can reduce the size of $n$ by a constant, and thus |
|
# make a smaller gameboard. this implementation doesn't really take |
|
# advantage of this, because it gets a bit complicated to do |
|
# correctly; therefore, we only fill this with one value. See |
|
# create_builder_value() further down. |
|
builder_values = set() |
|
|
|
# The following sets store gadget rectangles. They are of the form |
|
# { ((x0,y0),(x1,y1)) }. |
|
wire_gadget_rects = set() |
|
clause_gadget_rects = set() |
|
contra_clause_gadget_rects = set() |
|
double_clause_gadget_rects = set() |
|
forcing_gadget_rects = set() |
|
|
|
# The following sets store square placement coordinates for |
|
# particular gadgets, for optionally used metadata used in drawing. |
|
# They are of the form { (col,row) }. |
|
wire_placements = set() |
|
clause_placements = set() |
|
horizontal_wall_placements = set() |
|
vertical_wall_placements = set() |
|
forcing_gadget_placements = set() |
|
filler_placements = set() |
|
|
|
# The following sets store the tile-placement coordinates for |
|
# different gadgets. They are of the form { (col,row) }. The |
|
# coordinate is of the lower or left corner of the tile; the sets |
|
# with the name vtile in them are vertical tiles; those with the |
|
# name htile in them are horizontal tiles. |
|
filler_vtile_placements = set() |
|
filler_htile_placements = set() |
|
wire_witness_vtile_placements = set() |
|
wire_witness_htile_placements = set() |
|
clause_witness_vtile_placements = set() |
|
clause_witness_htile_placements = set() |
|
forcing_gadget_vtile_placements = set() |
|
forcing_gadget_htile_placements = set() |
|
|
|
|
|
# The following sets store the spine placements for different wires. |
|
# The horizontal wall stores the spines in the same format as a |
|
# vtile set. The vertical wall stores the spines in the same format |
|
# as a htile set. The horizontal_wall_ends stores the non-forced |
|
# ends of the horizontal wall. |
|
horizontal_wall_spines = set() |
|
horizontal_wall_spines_ends = set() |
|
vertical_wall_spines = set() |
|
|
|
# for each variable, x, stores the coordinates of the lower left |
|
# corner of the wire-gadget that represents it. |
|
x2gadget = {} |
|
|
|
# for each clause, with clause-index ci, stores the lower left |
|
# corner of the clause-gadget that represents it. |
|
ci2gadget = {} |
|
|
|
def increment_n(n0): |
|
""" |
|
Use this when more square-values are needed. |
|
|
|
Use it like: n = increment_n(n) |
|
""" |
|
|
|
assert n0 in tile_set_index |
|
n1 = n0 + 1 |
|
assert n1 not in tile_set_index |
|
|
|
# add new combinations to the tile-set |
|
|
|
tile_set.add( (n1,n1) ) |
|
tile_set_index[n1] = set([n1]) |
|
|
|
for i in range(n1): |
|
tile = (i,n1) |
|
tile_set.add(normalize_tile(tile)) |
|
|
|
tile_set_index[i].add(n1) |
|
tile_set_index[n1].add(i) |
|
|
|
|
|
return n1 |
|
|
|
def discharge_tile(tile): |
|
""" |
|
When you know for sure a tile can be layed, you call this to |
|
discharge the obligation from the tile-set. |
|
""" |
|
a,b = normalize_tile(tile) |
|
|
|
tile_set.discard((a,b)) |
|
tile_set_index[a].discard(b) |
|
tile_set_index[b].discard(a) |
|
|
|
def choose_discharge_tile(): |
|
""" |
|
Chooses a tile that remains in the tile-set. |
|
""" |
|
for tile in tile_set: |
|
return tile |
|
assert False |
|
|
|
|
|
def add_forcing_pair(pair): |
|
""" |
|
If a pair of square-values must be forced, call this with the |
|
pair. It will be forced, either in a force-gadget, or in the |
|
spine of a wire, if possible. |
|
""" |
|
a,b = normalize_tile(pair) |
|
|
|
forcing_pairs.add((a,b)) |
|
|
|
if a not in forcing_pairs_index: |
|
forcing_pairs_index[a] = set() |
|
if b not in forcing_pairs_index: |
|
forcing_pairs_index[b] = set() |
|
forcing_pairs_index[a].add(b) |
|
forcing_pairs_index[b].add(a) |
|
|
|
def discharge_forcing_pair(pair): |
|
""" |
|
When a pair that is supposed to be forced is known to be forced, |
|
call this to discharge it from the forcing obligation. |
|
""" |
|
a,b = normalize_tile(pair) |
|
forcing_pairs.discard((a,b)) |
|
|
|
forcing_pairs_index[a].discard(b) |
|
forcing_pairs_index[b].discard(a) |
|
|
|
def choose_forcing_pair(): |
|
""" |
|
Picks any pair from the set of pairs that require forcing. |
|
""" |
|
for forcing_pair in forcing_pairs: |
|
return forcing_pair |
|
assert False |
|
|
|
|
|
def create_builder_value(n): |
|
n = increment_n(n) |
|
add_forcing_pair( (n,n) ) |
|
|
|
for i in builder_values: |
|
add_forcing_pair( (i,n) ) |
|
|
|
builder_values.add(n) |
|
|
|
return n |
|
|
|
def choose_any_builder_value(): |
|
for builder_value in builder_values: |
|
return builder_value |
|
assert False |
|
|
|
# we start off with n=0 |
|
n = 0 |
|
# Let us use $0$ as special square-value $\mathcal T$ |
|
T = n; |
|
|
|
# Let us use $1$ as the builder value. We will only use one builder |
|
# value, since it is simpler to implement (but results in larger |
|
# gameboards) |
|
n = create_builder_value(n) |
|
# Let us make the builder value stand out |
|
square_names[n] = '$\\displaystyle 1$' |
|
|
|
# We need a special square-value for $\mathcal F$ |
|
n = increment_n(n) |
|
F = n |
|
|
|
# We can name $\mathcal T$ and $\mathcal F$ |
|
square_names[T] = '$\\mathcal T$' |
|
square_names[F] = '$\\mathcal F$' |
|
|
|
# We force (T,T), (T,F), and (F,F) to avoid inadvertant pairing in |
|
# the wires. |
|
add_forcing_pair((T,T)) |
|
add_forcing_pair((T,F)) |
|
add_forcing_pair((F,F)) |
|
|
|
|
|
# record of the square-values of the wire-variables, for each |
|
# clause. stored in the form {(x,ci) => (value,value')}, where |
|
# value' represents the negation of value. |
|
x_ci_2vars = {} |
|
|
|
# place the innards of wires first. |
|
def place_wire_innards(n,base_col,base_row): |
|
|
|
col = base_col |
|
row = base_row |
|
|
|
for x_j in x: |
|
rect_x0 = col |
|
|
|
x2gadget[x_j] = (col,row) |
|
|
|
# first place A* |
|
n = increment_n(n) |
|
Astar = n |
|
square_placements[(col,row+0)] = Astar |
|
square_placements[(col,row+1)] = Astar |
|
|
|
wire_placements.add( (col,row+0) ) |
|
wire_placements.add( (col,row+1) ) |
|
|
|
square_names[Astar] = '$A_{{ x_{{ {0} }} }}$'.format(x_j) |
|
|
|
if witness is not None: |
|
if x_j in witness: |
|
wire_witness_vtile_placements.add( (col,row) ) |
|
elif -x_j in witness: |
|
wire_witness_htile_placements.add( (col,row) ) |
|
wire_witness_htile_placements.add( (col,row+1) ) |
|
|
|
col += 1 |
|
|
|
for i,ci in enumerate(x2ci[x_j]): |
|
# for each clause x_j participates in, |
|
|
|
# make two vars, one for positive, one for negative |
|
n = increment_n(n) |
|
var = n |
|
|
|
n = increment_n(n) |
|
nvar = n |
|
|
|
# record this in a lookup table for later |
|
x_ci_2vars[(x_j,ci)] = (var,nvar) |
|
|
|
# place it on the board. |
|
square_placements[ (col+0, row + ((i+0) % 2)) ] = F |
|
square_placements[ (col+1, row + ((i+1) % 2)) ] = var |
|
square_placements[ (col+0, row + ((i+1) % 2)) ] = T |
|
square_placements[ (col+1, row + ((i+0) % 2)) ] = nvar |
|
|
|
|
|
square_names[var] = '$C_{{ {0}, x_{{ {1} }} }}$'.format(ci+1,x_j) |
|
square_names[nvar] = '$\\overline{{ C_{{ {0}, x_{{ {1} }} }} }}$'.format(ci+1,x_j) |
|
|
|
|
|
wire_placements.add( (col+0,row+0) ) |
|
wire_placements.add( (col+0,row+1) ) |
|
wire_placements.add( (col+1,row+0) ) |
|
wire_placements.add( (col+1,row+1) ) |
|
|
|
|
|
if witness is not None: |
|
if x_j in witness: |
|
wire_witness_htile_placements.add( (col+0,row+0) ) |
|
wire_witness_htile_placements.add( (col+0,row+1) ) |
|
elif -x_j in witness: |
|
wire_witness_htile_placements.add( (col+1,row+0) ) |
|
wire_witness_htile_placements.add( (col+1,row+1) ) |
|
col += 2 |
|
|
|
|
|
# total number of wire-variables for this x_j |
|
h = len(x2ci[x_j]) |
|
|
|
# add the last T,F, and two Astars |
|
square_placements[ (col+0, row + ((h+0) % 2)) ] = F |
|
square_placements[ (col+0, row + ((h+1) % 2)) ] = T |
|
square_placements[ (col+1, row + 0) ] = Astar |
|
square_placements[ (col+1, row + 1) ] = Astar |
|
|
|
wire_placements.add( (col+0, row + 0) ) |
|
wire_placements.add( (col+0, row + 1) ) |
|
wire_placements.add( (col+1, row + 0) ) |
|
wire_placements.add( (col+1, row + 1) ) |
|
|
|
if witness is not None: |
|
if x_j in witness: |
|
wire_witness_htile_placements.add( (col,row) ) |
|
wire_witness_htile_placements.add( (col,row+1) ) |
|
elif -x_j in witness: |
|
wire_witness_vtile_placements.add( (col+1,row) ) |
|
|
|
col += 2 |
|
|
|
rect_x1 = col |
|
|
|
wire_gadget_rects.add( ((rect_x0,row), (rect_x1,row+2)) ) |
|
|
|
# wall in between the wires |
|
col += 4 |
|
|
|
discharge_tile(normalize_tile((Astar,Astar))) |
|
discharge_tile(normalize_tile((Astar,T))) |
|
discharge_tile(normalize_tile((Astar,F))) |
|
|
|
|
|
return n,col |
|
|
|
def place_clause_innards(n,base_col,base_row): |
|
|
|
row = base_row |
|
col = base_col |
|
|
|
for ci,clause in enumerate(clauses): |
|
|
|
ci2gadget[ci] = (col,row) |
|
|
|
abc = [0] * 3 |
|
abcp = [0] * 3 |
|
for i,term in enumerate(clause): |
|
x_j = abs(term) |
|
|
|
x_j_square,nx_j_square = x_ci_2vars[(x_j,ci)] |
|
|
|
if term > 0: |
|
abc[i] = x_j_square |
|
abcp[i] = nx_j_square |
|
else: |
|
abc[i] = nx_j_square |
|
abcp[i] = x_j_square |
|
|
|
a,b,c = abc |
|
ap,bp,cp = abcp |
|
|
|
square_placements[ (col+0,row+0) ] = T |
|
square_placements[ (col+2,row+0) ] = T |
|
square_placements[ (col+1,row+1) ] = F |
|
|
|
square_placements[ (col+0,row+1) ] = a |
|
square_placements[ (col+1,row+0) ] = b |
|
square_placements[ (col+2,row+1) ] = c |
|
|
|
|
|
square_placements[ (col+1,row+2) ] = T |
|
square_placements[ (col+0,row+3) ] = F |
|
square_placements[ (col+2,row+3) ] = F |
|
|
|
square_placements[ (col+0,row+2) ] = ap |
|
square_placements[ (col+1,row+3) ] = bp |
|
square_placements[ (col+2,row+2) ] = cp |
|
|
|
for j in range(3): |
|
for k in range(4): |
|
clause_placements.add( (col+j,row+k) ) |
|
|
|
# discharge the tiles that are covered between the clause and the wire. |
|
discharge_tile(normalize_tile((a,T))) |
|
discharge_tile(normalize_tile((a,F))) |
|
discharge_tile(normalize_tile((ap,T))) |
|
discharge_tile(normalize_tile((ap,F))) |
|
|
|
discharge_tile(normalize_tile((b,T))) |
|
discharge_tile(normalize_tile((b,F))) |
|
discharge_tile(normalize_tile((bp,T))) |
|
discharge_tile(normalize_tile((bp,F))) |
|
|
|
discharge_tile(normalize_tile((c,T))) |
|
discharge_tile(normalize_tile((c,F))) |
|
discharge_tile(normalize_tile((cp,T))) |
|
discharge_tile(normalize_tile((cp,F))) |
|
|
|
double_clause_gadget_rects.add( ((col,row),(col+3,row+4)) ) |
|
clause_gadget_rects.add( ((col,row),(col+3,row+2)) ) |
|
contra_clause_gadget_rects.add( ((col,row+2),(col+3,row+4)) ) |
|
|
|
if witness is not None: |
|
|
|
true_term_index = None |
|
|
|
for i,term in enumerate(clause): |
|
if term in witness: |
|
|
|
if true_term_index is not None: |
|
print >> sys.stderr, 'ci:',ci, 'clause:',clause |
|
print >> sys.stderr, 'witness:',witness |
|
print >> sys.stderr, 'conflicts:', (term,clause[true_term_index]) |
|
assert true_term_index is None and "clause with two true terms" |
|
true_term_index = i |
|
|
|
if true_term_index is None: |
|
print >> sys.stderr, 'ci:',ci, 'clause:',clause |
|
print >> sys.stderr, 'witness:',witness |
|
|
|
assert true_term_index is not None and "clause with no true terms" |
|
|
|
# first calculate the tiles for the lower clause gadget |
|
htiles = set() |
|
vtiles = set() |
|
if true_term_index == 0: |
|
htiles |= set([ (col+0,row+0), (col+0,row+1) ]) |
|
vtiles |= set([ (col+2,row+0) ]) |
|
elif true_term_index == 1: |
|
vtiles |= set([ (col+0,row+0), (col+1,row+0), (col+2,row+0) ]) |
|
elif true_term_index == 2: |
|
htiles |= set([ (col+1,row+0), (col+1,row+1) ]) |
|
vtiles |= set([ (col+0,row+0) ]) |
|
else: |
|
assert False |
|
|
|
|
|
for htile in htiles: |
|
htile_col,htile_row = htile |
|
|
|
# add the tile to the witness placement |
|
clause_witness_htile_placements.add( (htile_col,htile_row) ) |
|
|
|
# add the same tile for the corresponding upper clause; |
|
# coincidentally, we can simply shift it the lower two tiles |
|
# up by two, and get the tiling. |
|
clause_witness_htile_placements.add( (htile_col,htile_row+2) ) |
|
for vtile in vtiles: |
|
vtile_col,vtile_row = vtile |
|
|
|
# add the tile to the witness placement |
|
clause_witness_vtile_placements.add( (vtile_col,vtile_row) ) |
|
|
|
# add the same tile for the corresponding upper clause |
|
clause_witness_vtile_placements.add( (vtile_col,vtile_row+2) ) |
|
|
|
|
|
|
|
|
|
col += 3 |
|
|
|
# wall between clauses |
|
col += 4 |
|
return n,col |
|
def choose_inner_builder_tile(n,builder_value): |
|
|
|
if builder_value in forcing_pairs_index: |
|
for pairing in forcing_pairs_index[builder_value]: |
|
if pairing == builder_value: |
|
continue |
|
|
|
return n,pairing |
|
|
|
n = increment_n(n) |
|
|
|
return n,n |
|
|
|
|
|
def place_vertical_wall(n,builder_value,base_col,base_row,length): |
|
|
|
col = base_col |
|
row = base_row |
|
|
|
for i in range(length): |
|
|
|
n,a = choose_inner_builder_tile(n,builder_value); |
|
n,b = choose_inner_builder_tile(n,builder_value); |
|
|
|
|
|
square_placements[ (col+0,row) ] = a |
|
square_placements[ (col+1,row) ] = builder_value |
|
square_placements[ (col+2,row) ] = builder_value |
|
square_placements[ (col+3,row) ] = b |
|
|
|
vertical_wall_placements.add( (col+0,row) ) |
|
vertical_wall_placements.add( (col+1,row) ) |
|
vertical_wall_placements.add( (col+2,row) ) |
|
vertical_wall_placements.add( (col+3,row) ) |
|
|
|
discharge_tile(normalize_tile((a,builder_value))) |
|
discharge_tile(normalize_tile((b,builder_value))) |
|
|
|
vertical_wall_spines.add( (col,row) ) |
|
vertical_wall_spines.add( (col+2,row) ) |
|
|
|
row += 1 |
|
|
|
return n |
|
|
|
def place_horizontal_wall(n,builder_value,base_col,base_row,length): |
|
|
|
col = base_col |
|
row = base_row |
|
|
|
for i in range(length): |
|
|
|
n,a = choose_inner_builder_tile(n,builder_value); |
|
n,b = choose_inner_builder_tile(n,builder_value); |
|
|
|
|
|
square_placements[ (col,row+0) ] = a |
|
square_placements[ (col,row+1) ] = builder_value |
|
square_placements[ (col,row+2) ] = builder_value |
|
square_placements[ (col,row+3) ] = b |
|
|
|
horizontal_wall_placements.add( (col,row+0) ) |
|
horizontal_wall_placements.add( (col,row+1) ) |
|
horizontal_wall_placements.add( (col,row+2) ) |
|
horizontal_wall_placements.add( (col,row+3) ) |
|
|
|
discharge_tile(normalize_tile((a,builder_value))) |
|
discharge_tile(normalize_tile((b,builder_value))) |
|
|
|
if i + 1 < length and (i > 0 or base_col == 0): |
|
horizontal_wall_spines.add( (col,row) ) |
|
horizontal_wall_spines.add( (col,row+2) ) |
|
else: |
|
horizontal_wall_spines_ends.add( (col,row) ) |
|
horizontal_wall_spines_ends.add( (col,row+2) ) |
|
col += 1 |
|
|
|
|
|
|
|
return n |
|
|
|
|
|
def place_clause_walls(n,builder_value,base_col,base_row): |
|
|
|
row = base_row |
|
col = base_col |
|
|
|
for ci,clause in enumerate(clauses): |
|
|
|
col += 3 |
|
|
|
|
|
# wall between clauses |
|
n = place_vertical_wall(n,builder_value,base_col=col, base_row=base_row, length=4) |
|
col += 4 |
|
return n |
|
def place_wire_walls(n,builder_value,base_col,base_row): |
|
|
|
col = base_col |
|
row = base_row |
|
|
|
for x_j in x: |
|
|
|
col += 1 |
|
|
|
for i,ci in enumerate(x2ci[x_j]): |
|
col += 2 |
|
|
|
|
|
col += 2 |
|
|
|
|
|
# wall between clauses |
|
n = place_vertical_wall(n,builder_value,base_col=col, base_row=base_row, length=2) |
|
# skip the wall |
|
col += 4 |
|
return n |
|
|
|
def place_forcing_gadgets(n,base_col,base_row): |
|
|
|
col = base_col |
|
row = base_row |
|
|
|
|
|
while len(forcing_pairs) > 0: |
|
a,b = choose_forcing_pair() |
|
discharge_forcing_pair((a,b)) |
|
|
|
n = increment_n(n) |
|
c = n |
|
|
|
n = increment_n(n) |
|
d = n |
|
|
|
square_placements[ (col+0,row+0) ] = b |
|
square_placements[ (col+1,row+0) ] = a |
|
square_placements[ (col+2,row+0) ] = b |
|
square_placements[ (col+1,row+1) ] = b |
|
square_placements[ (col+0,row+1) ] = c |
|
square_placements[ (col+2,row+1) ] = d |
|
|
|
forcing_gadget_placements.add( (col+0,row+0) ) |
|
forcing_gadget_placements.add( (col+1,row+0) ) |
|
forcing_gadget_placements.add( (col+2,row+0) ) |
|
forcing_gadget_placements.add( (col+0,row+1) ) |
|
forcing_gadget_placements.add( (col+1,row+1) ) |
|
forcing_gadget_placements.add( (col+2,row+1) ) |
|
|
|
discharge_tile(normalize_tile((a,b))) |
|
discharge_tile(normalize_tile((b,c))) |
|
discharge_tile(normalize_tile((b,d))) |
|
|
|
forcing_gadget_rects.add( ((col,row),(col+3,row+2)) ) |
|
|
|
forcing_gadget_vtile_placements.add( (col+0,row) ) |
|
forcing_gadget_vtile_placements.add( (col+1,row) ) |
|
forcing_gadget_vtile_placements.add( (col+2,row) ) |
|
|
|
col += 3 |
|
return n,col |
|
|
|
def discharge_and_fill_all_tiles(): |
|
|
|
# we are going to be placing vertical tiles in rows, startin |
|
# from the bottom |
|
for row in range(0,n+1,2): |
|
|
|
# simply tile from right-to-left until we hit a square |
|
for col in reversed(range(0,n+1)): |
|
|
|
if (col,row) in square_placements: |
|
# if we hit a square, go to next row. |
|
break |
|
|
|
assert (col,row+1) not in square_placements |
|
assert len(tile_set) > 0 |
|
|
|
# pick a tile/square to lay down |
|
tile = choose_discharge_tile() |
|
|
|
a,b = tile |
|
|
|
# place the squares |
|
square_placements[(col,row+0)] = a |
|
square_placements[(col,row+1)] = b |
|
|
|
# record the purpose of these squares |
|
filler_placements.add((col,row+0)) |
|
filler_placements.add((col,row+1)) |
|
|
|
# record the tile |
|
filler_vtile_placements.add((col,row)) |
|
|
|
discharge_tile(tile) |
|
|
|
# sometimes the top rows cannot be tiled vertically, because |
|
# there is only one row left |
|
if (n+2) & 1 == 1: |
|
row = n+2-1 |
|
for col in range(0,n+1,2): |
|
|
|
assert (col+0,row) not in square_placements |
|
assert (col+1,row) not in square_placements |
|
|
|
assert len(tile_set) > 0 |
|
|
|
tile = choose_discharge_tile() |
|
|
|
a,b = tile |
|
|
|
square_placements[(col+0,row)] = a |
|
square_placements[(col+1,row)] = b |
|
filler_placements.add((col+0,row)) |
|
filler_placements.add((col+1,row)) |
|
filler_htile_placements.add( (col,row) ) |
|
|
|
discharge_tile(tile) |
|
|
|
# make sure we discharged the entire tile-set |
|
assert len(tile_set) == 0 |
|
|
|
base_col = 0 |
|
# first two rows are for forced tiles. |
|
# next 4 rows are a wall, straight across the board. |
|
# the 6th row is the variables and clauses. |
|
base_row = 2 + 4 |
|
n,wire_board_width = place_wire_innards(n,base_col,base_row) |
|
|
|
|
|
base_col = 0 |
|
# after the wire rows, we put a wall across the board, then |
|
# place the clauses in its own 2 rows |
|
base_row = 2 + 4 + 2 + 4 |
|
n,clause_board_width = place_clause_innards(n,base_col,base_row) |
|
|
|
|
|
base_col = 0 |
|
base_row = 0 |
|
n,forced_gadgets_board_width = place_forcing_gadgets(n,base_col,base_row) |
|
|
|
min_width = max([wire_board_width,clause_board_width,forced_gadgets_board_width]) |
|
|
|
|
|
|
|
|
|
|
|
builder_value = choose_any_builder_value() |
|
|
|
# place a wall over the bottom-most two squares, across the entire |
|
# grid |
|
base_col = 0 |
|
base_row = 2 |
|
n = place_horizontal_wall(n,builder_value,base_col,base_row,min_width+1) |
|
|
|
|
|
|
|
# place a wall over the wire-variables, across the entire grid |
|
base_col = 0 |
|
base_row = 8 |
|
n = place_horizontal_wall(n,builder_value,base_col,base_row,min_width+1) |
|
|
|
# place a wall over the clauses, across the entire grid |
|
base_col = 0 |
|
base_row = 8+4+4 |
|
n = place_horizontal_wall(n,builder_value,base_col,base_row,min_width+1) |
|
|
|
# place vertical walls between clauses |
|
base_col = 0 |
|
base_row = 2 + 4 + 2 + 4 |
|
n = place_clause_walls(n,builder_value,base_col=base_col,base_row=base_row) |
|
|
|
# place vertical walls between wires |
|
base_col = 0 |
|
base_row = 2 + 4 |
|
n = place_wire_walls(n,builder_value,base_col=base_col,base_row=base_row) |
|
|
|
if increment_extra_n: |
|
n = increment_n(n) |
|
|
|
# tile the rest of the board |
|
discharge_and_fill_all_tiles() |
|
|
|
|
|
|
|
witness_htiles = set() |
|
witness_vtiles = set() |
|
|
|
witness_htiles |= forcing_gadget_htile_placements |
|
witness_htiles |= clause_witness_htile_placements |
|
witness_htiles |= filler_htile_placements |
|
witness_htiles |= vertical_wall_spines |
|
|
|
|
|
witness_vtiles |= forcing_gadget_vtile_placements |
|
witness_vtiles |= clause_witness_vtile_placements |
|
witness_vtiles |= filler_vtile_placements |
|
witness_vtiles |= horizontal_wall_spines |
|
witness_vtiles |= horizontal_wall_spines_ends |
|
|
|
details = {} |
|
details['n'] = n |
|
details['square_placements'] = square_placements |
|
details['square_names'] = square_names |
|
details['min_width'] = min_width |
|
details['x2gadget'] = x2gadget |
|
details['ci2gadget'] = ci2gadget |
|
details['x2ci'] = x2ci |
|
|
|
|
|
details['wire_gadget_rects'] = wire_gadget_rects |
|
details['clause_gadget_rects'] = clause_gadget_rects |
|
details['contra_clause_gadget_rects'] = contra_clause_gadget_rects |
|
details['double_clause_gadget_rects'] = double_clause_gadget_rects |
|
details['forcing_gadget_rects'] = forcing_gadget_rects |
|
|
|
|
|
details['wire_placements'] = wire_placements |
|
details['clause_placements'] = clause_placements |
|
details['horizontal_wall_placements'] = horizontal_wall_placements |
|
details['vertical_wall_placements'] = vertical_wall_placements |
|
details['forcing_gadget_placements'] = forcing_gadget_placements |
|
details['filler_placements'] = filler_placements |
|
|
|
|
|
details['filler_vtile_placements'] = filler_vtile_placements |
|
details['filler_htile_placements'] = filler_htile_placements |
|
details['wire_witness_htile_placements'] = wire_witness_htile_placements |
|
details['wire_witness_vtile_placements'] = wire_witness_vtile_placements |
|
details['clause_witness_htile_placements'] = clause_witness_htile_placements |
|
details['clause_witness_vtile_placements'] = clause_witness_vtile_placements |
|
details['forcing_gadget_htile_placements'] = forcing_gadget_htile_placements |
|
details['forcing_gadget_vtile_placements'] = forcing_gadget_vtile_placements |
|
|
|
details['horizontal_wall_spines'] = horizontal_wall_spines |
|
details['vertical_wall_spines'] = vertical_wall_spines |
|
details['horizontal_wall_spines_ends'] = horizontal_wall_spines_ends |
|
|
|
details['witness_htiles'] = witness_htiles |
|
details['witness_vtiles'] = witness_vtiles |
|
|
|
|
|
|
|
return details |
|
|
|
|
|
def draw_rect_at(rect,tikz): |
|
(x0,y0),(x1,y1) = rect |
|
|
|
tikzrect = '\\draw[wline] ({x0},{y0}) rectangle ({x1},{y1});\n' |
|
tikzrect = tikzrect.format(x0=x0,y0=y0,x1=x1,y1=y1) |
|
tikz += [' ',tikzrect] |
|
def mark_rect_at(rect,tikz): |
|
(x0,y0),(x1,y1) = rect |
|
|
|
tikzrect = '\\draw[wline,dashed] ({x0},{y0}) rectangle ({x1},{y1});\n' |
|
tikzrect = tikzrect.format(x0=x0,y0=y0,x1=x1,y1=y1) |
|
tikz += [' ',tikzrect] |
|
|
|
def draw_vtile_at((col,row),tikz): |
|
tikzBorder = '\\vtile{{ {col} }}{{ {row} }};' |
|
tikzBorder = tikzBorder.format(col=col,row=row) |
|
tikz += [' ',tikzBorder,'\n'] |
|
def draw_htile_at((col,row),tikz): |
|
tikzBorder = '\\htile{{ {col} }}{{ {row} }};' |
|
tikzBorder = tikzBorder.format(col=col,row=row) |
|
tikz += [' ',tikzBorder,'\n'] |
|
def draw_dvtile_at((col,row),tikz): |
|
tikzBorder = '\\dvtile{{ {col} }}{{ {row} }};' |
|
tikzBorder = tikzBorder.format(col=col,row=row) |
|
tikz += [' ',tikzBorder,'\n'] |
|
def draw_dhtile_at((col,row),tikz): |
|
tikzBorder = '\\dhtile{{ {col} }}{{ {row} }};' |
|
tikzBorder = tikzBorder.format(col=col,row=row) |
|
tikz += [' ',tikzBorder,'\n'] |
|
|
|
def draw_dominosa_to_tikz_strlist(details,**kwargs): |
|
""" |
|
Produces a list of strings that contain a TikZ drawing of the |
|
dominosa board. |
|
|
|
See code for arguments. Arguments are self-documenting. |
|
|
|
Uses: |
|
|
|
* TeX packages: intcalc,calc,tikz,verbatim,amsmath,amssymb,relsize |
|
* TikZ libraries: arrows,decorations.pathreplacing,shapes,fadings |
|
|
|
""" |
|
|
|
|
|
|
|
use_names = kwargs.pop('use_names',True) |
|
|
|
clip_top = kwargs.pop('clip_top',False) |
|
clip_right = kwargs.pop('clip_right',False) |
|
|
|
clip_x0 = kwargs.pop('clip_x0',None) |
|
clip_x1 = kwargs.pop('clip_x1',None) |
|
clip_y0 = kwargs.pop('clip_y0',None) |
|
clip_y1 = kwargs.pop('clip_y1',None) |
|
|
|
|
|
draw_wires = kwargs.pop('draw_wires',True) |
|
draw_clauses = kwargs.pop('draw_clauses',True) |
|
draw_forcing_gadgets = kwargs.pop('draw_forcing_gadgets',True) |
|
draw_horizontal_walls = kwargs.pop('draw_horizontal_walls',True) |
|
draw_vertical_walls = kwargs.pop('draw_vertical_walls',True) |
|
draw_filler_squares = kwargs.pop('draw_filler_squares',False) |
|
draw_all_squares = kwargs.pop('draw_all_squares',True) |
|
|
|
draw_wire_gadgets = kwargs.pop('draw_wire_gadgets',True) |
|
draw_double_clause_gadgets = kwargs.pop('draw_double_clause_gadgets',True) |
|
draw_clause_gadgets = kwargs.pop('draw_clause_gadgets',False) |
|
draw_contra_clause_gadgets = kwargs.pop('draw_contra_clause_gadgets',False) |
|
mark_forcing_gadgets = kwargs.pop('mark_forcing_gadgets',True) |
|
|
|
tile_horizontal_walls = kwargs.pop('tile_horizontal_walls',True) |
|
tile_horizontal_wall_ends = kwargs.pop('tile_horizontal_wall_ends',False) |
|
tile_vertical_walls = kwargs.pop('tile_vertical_walls',True) |
|
tile_filler_htiles = kwargs.pop('tile_filler_htiles',False) |
|
tile_filler_vtiles = kwargs.pop('tile_filler_vtiles',False) |
|
tile_forcing_gadgets = kwargs.pop('tile_forcing_gadgets',False) |
|
tile_witness = kwargs.pop('tile_witness',False) |
|
tile_witness_on_wires = kwargs.pop('tile_witness_on_wires',tile_witness) |
|
tile_witness_on_clauses = kwargs.pop('tile_witness_on_clauses',tile_witness) |
|
|
|
|
|
|
|
|
|
if len(kwargs) > 0: |
|
print >> sys.stderr, 'kwargs.keys():' |
|
print >> sys.stderr, kwargs.keys() |
|
assert len(kwargs) == 0 and "At least one invalid argument" |
|
|
|
|
|
|
|
square_placements = details['square_placements'] |
|
square_names = details['square_names'] if use_names else {} |
|
n = details['n'] |
|
min_width = details['min_width'] |
|
|
|
|
|
# squares |
|
wire_placements = details['wire_placements'] if draw_wires else set() |
|
clause_placements = details['clause_placements'] if draw_clauses else set() |
|
forcing_gadget_placements = details['forcing_gadget_placements'] if draw_forcing_gadgets else set() |
|
horizontal_wall_placements = details['horizontal_wall_placements'] if draw_horizontal_walls else set() |
|
vertical_wall_placements = details['vertical_wall_placements'] if draw_vertical_walls else set() |
|
horizontal_wall_placements = details['horizontal_wall_placements'] if draw_horizontal_walls else set() |
|
filler_placements = details['filler_placements'] if draw_filler_squares else set() |
|
|
|
# gadget borders |
|
wire_gadget_rects = details['wire_gadget_rects'] if draw_wire_gadgets else set() |
|
clause_gadget_rects = details['clause_gadget_rects'] if draw_clause_gadgets else set() |
|
contra_clause_gadget_rects = details['contra_clause_gadget_rects'] if draw_contra_clause_gadgets else set() |
|
double_clause_gadget_rects = details['double_clause_gadget_rects'] if draw_double_clause_gadgets else set() |
|
forcing_gadget_rects = details['forcing_gadget_rects'] if mark_forcing_gadgets else set() |
|
|
|
# tiles |
|
horizontal_wall_spines = details['horizontal_wall_spines'] if tile_horizontal_walls else set() |
|
horizontal_wall_spines_ends = details['horizontal_wall_spines_ends'] if tile_horizontal_wall_ends else set() |
|
vertical_wall_spines = details['vertical_wall_spines'] if tile_vertical_walls else set() |
|
wire_witness_vtile_placements = details['wire_witness_vtile_placements'] if tile_witness_on_wires else set() |
|
wire_witness_htile_placements = details['wire_witness_htile_placements'] if tile_witness_on_wires else set() |
|
clause_witness_vtile_placements = details['clause_witness_vtile_placements'] if tile_witness_on_clauses else set() |
|
clause_witness_htile_placements = details['clause_witness_htile_placements'] if tile_witness_on_clauses else set() |
|
filler_htile_placements = details['filler_htile_placements'] if tile_filler_vtiles else set() |
|
filler_vtile_placements = details['filler_vtile_placements'] if tile_filler_htiles else set() |
|
forcing_gadget_htile_placements = details['forcing_gadget_htile_placements'] if tile_forcing_gadgets else set() |
|
forcing_gadget_vtile_placements = details['forcing_gadget_vtile_placements'] if tile_forcing_gadgets else set() |
|
|
|
|
|
|
|
top = n + 3 |
|
right = n + 2 |
|
|
|
|
|
|
|
tikz = [] |
|
|
|
|
|
if clip_top: |
|
clip_y1 = min([top,22,clip_y1]) if clip_y1 is not None else min([top,22]) |
|
if clip_right: |
|
clip_x1 = min([right,min_width+1+2,clip_x1]) if clip_x1 is not None else min([right,min_width+1+2]) |
|
|
|
|
|
if clip_x0 is not None or clip_x1 is not None \ |
|
or clip_y0 is not None or clip_y1 is not None: |
|
if clip_x0 is None: |
|
clip_x0 = -1 |
|
if clip_y0 is None: |
|
clip_y0 = -1 |
|
if clip_x1 is None: |
|
clip_x1 = right |
|
if clip_y1 is None: |
|
clip_y1 = top |
|
|
|
tikzclip = '\\clip ({x0},{y0}) rectangle ({x1},{y1});\n\n' |
|
tikzclip = tikzclip.format(x0=clip_x0, |
|
y0=clip_y0, |
|
x1=clip_x1, |
|
y1=clip_y1) |
|
|
|
tikz += [tikzclip] |
|
|
|
if clip_x0 is None: |
|
clip_x0 = -1 |
|
if clip_y0 is None: |
|
clip_y0 = -1 |
|
if clip_x1 is None: |
|
clip_x1 = right |
|
if clip_y1 is None: |
|
clip_y1 = top |
|
|
|
|
|
def draw_square_at((col,row),tikz): |
|
|
|
value = square_placements[(col,row)] |
|
|
|
name = value if value not in square_names else square_names[value] |
|
|
|
tikzDsquare = '\\Dsquare{{ {i} }}{{ {j} }}{{ {value} }};\n' |
|
tikzDsquare = tikzDsquare.format(i=col,j=row,value=name) |
|
tikz += [' ',tikzDsquare] |
|
|
|
def in_clip((col,row)): |
|
if col < clip_x0 or col > clip_x1: |
|
return False |
|
if row < clip_y0 or row > clip_y1: |
|
return False |
|
return True |
|
|
|
for (col,row) in wire_placements: |
|
draw_square_at((col,row),tikz) |
|
for (col,row) in clause_placements: |
|
draw_square_at((col,row),tikz) |
|
for (col,row) in forcing_gadget_placements: |
|
draw_square_at((col,row),tikz) |
|
for (col,row) in horizontal_wall_placements: |
|
draw_square_at((col,row),tikz) |
|
for (col,row) in vertical_wall_placements: |
|
draw_square_at((col,row),tikz) |
|
|
|
if draw_all_squares: |
|
|
|
for col in range(clip_x1): |
|
for row in range(clip_y1): |
|
|
|
if not in_clip((col,row)): |
|
continue |
|
|
|
draw_square_at((col,row),tikz) |
|
|
|
|
|
|
|
for (col,row) in filler_placements: |
|
if not in_clip((col,row)): |
|
continue |
|
draw_square_at((col,row),tikz) |
|
|
|
|
|
for rect in wire_gadget_rects: |
|
draw_rect_at(rect,tikz) |
|
for rect in clause_gadget_rects: |
|
draw_rect_at(rect,tikz) |
|
for rect in contra_clause_gadget_rects: |
|
draw_rect_at(rect,tikz) |
|
for rect in double_clause_gadget_rects: |
|
draw_rect_at(rect,tikz) |
|
|
|
for rect in forcing_gadget_rects: |
|
mark_rect_at(rect,tikz) |
|
|
|
vtiles = set() |
|
htiles = set() |
|
|
|
# dashed tiles |
|
dvtiles = set() |
|
dhtiles = set() |
|
for (col,row) in horizontal_wall_spines: |
|
vtiles.add((col,row)) |
|
for (col,row) in horizontal_wall_spines_ends: |
|
dvtiles.add((col,row)) |
|
for (col,row) in vertical_wall_spines: |
|
htiles.add((col,row)) |
|
|
|
for (col,row) in wire_witness_htile_placements: |
|
htiles.add((col,row)) |
|
for (col,row) in wire_witness_vtile_placements: |
|
vtiles.add((col,row)) |
|
|
|
for (col,row) in clause_witness_htile_placements: |
|
htiles.add((col,row)) |
|
for (col,row) in clause_witness_vtile_placements: |
|
vtiles.add((col,row)) |
|
|
|
for (col,row) in filler_htile_placements: |
|
dhtiles.add((col,row)) |
|
for (col,row) in filler_vtile_placements: |
|
dvtiles.add((col,row)) |
|
|
|
for (col,row) in forcing_gadget_htile_placements: |
|
dhtiles.add((col,row)) |
|
for (col,row) in forcing_gadget_vtile_placements: |
|
dvtiles.add((col,row)) |
|
|
|
|
|
|
|
|
|
# draw tiles |
|
for (col,row) in htiles: |
|
if not in_clip((col,row)) and not in_clip((col+1,row)): |
|
continue |
|
draw_htile_at((col,row),tikz) |
|
for (col,row) in vtiles: |
|
if not in_clip((col,row)) and not in_clip((col,row+1)): |
|
continue |
|
draw_vtile_at((col,row),tikz) |
|
|
|
# draw dashed tiles |
|
for (col,row) in dhtiles: |
|
if not in_clip((col,row)) and not in_clip((col+1,row)): |
|
continue |
|
draw_dhtile_at((col,row),tikz) |
|
for (col,row) in dvtiles: |
|
if not in_clip((col,row)) and not in_clip((col,row+1)): |
|
continue |
|
draw_dvtile_at((col,row),tikz) |
|
|
|
|
|
# bottom border |
|
tikzBorder = '\\fill[brect,top color=black,bottom color=white] (-1,-1) rectangle ({n2},0);' |
|
tikzBorder = tikzBorder.format(n2=n+2) |
|
tikz += [' ',tikzBorder,'\n'] |
|
|
|
|
|
# top border |
|
tikzBorder = '\\fill[brect,bottom color=black,top color=white] (-1,{n2}) rectangle ({n2},{n3});' |
|
tikzBorder = tikzBorder.format(n2=n+2,n3=n+3) |
|
tikz += [' ',tikzBorder,'\n'] |
|
|
|
# left border |
|
tikzBorder = '\\fill[brect,right color=black,left color=white] (-1,-1) rectangle (0,{n3});' |
|
tikzBorder = tikzBorder.format(n3=n+3) |
|
tikz += [' ',tikzBorder,'\n'] |
|
|
|
# right border |
|
tikzBorder = '\\fill[brect,left color=black,right color=white] ({n1},-1) rectangle ({n2},{n3});' |
|
tikzBorder = tikzBorder.format(n1=n+1,n2=n+2,n3=n+3) |
|
tikz += [' ',tikzBorder,'\n'] |
|
|
|
|
|
return tikz |
|
|
|
|
|
tikztemplate_top = r""" |
|
\documentclass[tikz,margin=5em]{standalone} |
|
\usepackage{intcalc,calc} |
|
\usepackage{tikz} |
|
\usepackage{verbatim} |
|
\usepackage{amsmath,amssymb} |
|
|
|
\usetikzlibrary{arrows} |
|
\usetikzlibrary{decorations.pathreplacing} |
|
\usepackage{relsize} |
|
|
|
|
|
|
|
\usetikzlibrary{shapes} |
|
|
|
|
|
|
|
|
|
\usetikzlibrary{fadings} |
|
|
|
|
|
\title{Dominosa.TikZ.template} |
|
|
|
\begin{document} |
|
""" |
|
|
|
tikztemplate_bottom = r"""\end{document}""" |
|
|
|
tikz_commands = r""" |
|
|
|
\def\tilecolor{gray} |
|
\def\squarecolor{gray!40!white} |
|
|
|
\tikzset{dstyle/.style={line width=0.5 mm,rounded corners,fill=gray,fill opacity=.3}} |
|
\tikzset{snode/.style={rounded corners,fill=\squarecolor, |
|
text opacity=1,fill opacity=1, |
|
minimum size=1 cm,font=\bfseries}} |
|
\tikzset{wline/.style={rounded corners,line width=1mm,color=blue!40!black}} |
|
\tikzset{bline/.style={rounded corners,line width=.5mm,color=black}} |
|
\tikzset{brect/.style={fill=black}} |
|
\tikzset{darrow/.style={-stealth,line width=.5mm}} |
|
\tikzset{idarrow/.style={stealth-,line width=.5mm}} |
|
\tikzset{gstyle/.style={step=1cm,black,line width=.1mm,opacity=.3}} |
|
|
|
\tikzset{checknode/.style = {font=\relsize{#1}}} |
|
\tikzset{descarrow/.style={-,line width=1mm,orange}} |
|
\tikzset{descnode/.style={ellipse,fill=green!20,fill opacity=0.7}} |
|
|
|
|
|
\def\tilemargin{.05} |
|
\def\kleene{${\star}$} |
|
\def\numeralA{$\mathbf{1}$} |
|
\def\qkleene{${\mathbf q}{\star}$} |
|
\def\rkleene{${\mathbf r}{\star}$} |
|
\def\cornerLength{5} |
|
\def\tkleene{${\mathcal T}{\star}$} |
|
\def\fkleene{${\mathcal F}{\star}$} |
|
\def\tvalue{$\mathcal T$} |
|
\def\fvalue{$\mathcal F$} |
|
|
|
\def\wificolor{green!20!blue!10} |
|
|
|
|
|
|
|
|
|
\newcommand*{\vtile}[2]{ |
|
\def\x{#1} |
|
\def\y{#2} |
|
|
|
|
|
\draw[dstyle] (\x+\tilemargin,\y+\tilemargin) rectangle (\x+1-\tilemargin,\y+2-\tilemargin); |
|
} |
|
\newcommand*{\dvtile}[2]{ |
|
\def\x{#1} |
|
\def\y{#2} |
|
|
|
|
|
\draw[dstyle,dashed] (\x+\tilemargin,\y+\tilemargin) rectangle (\x+1-\tilemargin,\y+2-\tilemargin); |
|
} |
|
|
|
|
|
|
|
\newcommand*{\htile}[2]{ |
|
\def\x{#1} |
|
\def\y{#2} |
|
|
|
|
|
\draw[dstyle] (\x+\tilemargin,\y+\tilemargin) rectangle (\x+2-\tilemargin,\y+1-\tilemargin); |
|
} |
|
\newcommand*{\dhtile}[2]{ |
|
\def\x{#1} |
|
\def\y{#2} |
|
|
|
|
|
\draw[dstyle,dashed] (\x+\tilemargin,\y+\tilemargin) rectangle (\x+2-\tilemargin,\y+1-\tilemargin); |
|
} |
|
|
|
|
|
\newcommand*{\Dsquare}[3]{ |
|
\def\x{#1} |
|
\def\y{#2} |
|
\def\sname{#3} |
|
|
|
|
|
\node[snode] at (\x+.5,\y+.5) {\sname}; |
|
} |
|
|
|
|
|
|
|
|
|
""" |
|
|
|
def tikz_passed(): |
|
return [r""" |
|
\begin{scope}[opacity=.7,transparency group] |
|
\node [font=\fontsize{3cm}{3cm},circle,draw=green,line width=2ex,minimum size=4cm,color=green] |
|
at (4,4.5) |
|
{$\checkmark$}; |
|
\node [font=\fontsize{10cm}{10cm},color=green] |
|
at (4,4.5) |
|
{\Huge PASSED}; |
|
\end{scope} |
|
"""] |
|
|
|
def tikz_failed(): |
|
return [r""" |
|
\begin{scope}[opacity=.7,transparency group] |
|
\node [font=\fontsize{10cm}{10cm},forbidden sign,line width=2ex,minimum size=4cm,draw=red,color=red] |
|
at (4,4.5) |
|
{\Huge FAILED}; |
|
\end{scope} |
|
"""] |
|
|
|
|
|
|
|
|
|
|
|
def main(): |
|
|
|
pass |
|
|