Download
language ESSENCE' 1.0
$ ---- Parameters & Constants ----
$ **** Allowing 0 here for testing purposes only
given initGrid : matrix indexed by[int(1..gridHeight), int(1..gridWidth)] of int(0..)
letting GRIDCOLS be domain int(1..gridWidth)
letting GRIDROWS be domain int(1..gridHeight)
letting NOBLOCKS be gridWidth * gridHeight
letting COLOURS be domain int(1..max(flatten(initGrid)))
letting EMPTY be 0
letting EMPTYANDCOLOURS be domain int(EMPTY) union COLOURS
given goalBlocksRemaining : int(0..NOBLOCKS)
given noSteps : int(1..)
letting STEPSFROM1 be domain int(1..noSteps)
letting STEPSFROM0 be domain int(0..noSteps)
$ ---- Decision Variables ----
$ NB Actual game sometimes restricted firing positions.
find fpRow : matrix indexed by[STEPSFROM1] of int(0..gridHeight)
find fpCol : matrix indexed by[STEPSFROM1] of int(0..gridWidth)
find grid : matrix indexed by[STEPSFROM0, GRIDROWS, GRIDCOLS]
of EMPTYANDCOLOURS
find hand : matrix indexed by[STEPSFROM0] of COLOURS
$ ---- Constraints -------------------------------------------------------------
such that
$ ---- Initial & Goal States ----------------------------
$ Initial state:
forAll gCol : GRIDCOLS .
forAll gRow : GRIDROWS .
grid[0, gRow, gCol] = initGrid[gRow, gCol],
$ Goal state:
atleast(flatten(grid[noSteps,..,..]),
[NOBLOCKS - goalBlocksRemaining],
[EMPTY]),
$ ---- End of Initial & Goal States ---------------------
$ ---- Constrain Actions --------------------------------
$ Each move must do something useful:
$ Careful: if not counting 0s then it will try and insert phantom colours.
forAll step : STEPSFROM1 .
sum(flatten(grid[step-1,..,..])) > sum(flatten(grid[step,..,..])),
$ Exactly one fp axis must be 0. (exclusive OR, only ONE fired angle)
forAll step : STEPSFROM1 .
(
(fpRow[step] * fpCol[step]) = 0 /\ (fpRow[step] + fpCol[step]) > 0
),
$ ---- End of Constrain Actions -------------------------
$ Column Shot
forAll step : STEPSFROM1 .
(fpCol[step] > 0)
->
$ All other columns are untouched.
(forAll col : GRIDCOLS .
(col != fpCol[step]) ->
(forAll row : GRIDROWS . grid[step,row,col] = grid[step-1,row,col])
) /\
$ Must exist a row where grid[step-1,row,fpCol[step]] = hand.
(exists row : GRIDROWS .
(grid[step-1,row,fpCol[step]] = hand[step-1]) /\
$ Everything above is empty or same colour as the hand.
(forAll above : int(1..row-1) .
grid[step-1,above,fpCol[step]] = EMPTY \/
grid[step-1,above,fpCol[step]] = hand[step-1]) /\
$ Effect is to make everything down to this row empty
(forAll clear : int(1..row) . grid[step,clear,fpCol[step]] = EMPTY) /\
($ Either this is bottom in which case hand remains same.
(row = gridHeight) /\ (hand[step] = hand[step-1])
\/
$ Or the next row down is of a different colour, swaps with hand.
(grid[step-1,row+1,fpCol[step]] != hand[step-1] /\
grid[step,row+1,fpCol[step]] = hand[step-1] /\
hand[step] = grid[step-1,row+1,fpCol[step]] /\
forAll below : int(row+2..gridHeight) .
grid[step,below,fpCol[step]] = grid[step-1,below,fpCol[step]])
)
),
$ Row Shot
$ We identify the column that is *different* from the hand, if one exists. This
$ separates us from wall fall and saves us from worrying about gaps after the
$ last consumed block.
forAll step : STEPSFROM1 .
(fpRow[step] > 0)
->
(exists col : GRIDCOLS .
$ Preconditions: a col with a block different from hand, to the left
$ all empty/hand colour, but there must exist a block of hand colour.
(
(grid[step-1,fpRow[step],col] != hand[step-1]) /\
(forAll left : int(1..col-1) .
grid[step-1,fpRow[step],left] = EMPTY \/
grid[step-1,fpRow[step],left] = hand[step-1]) /\
(exists left : int(1..col-1) .
grid[step-1,fpRow[step],left] = hand[step-1])
)
/\
$ Effects:
($ left: Blocks falling, staying fixed.
$ NB If nothing above then replacing with up still works: it is EMPTY
$ So split is on fpRow[step] > 1. Irrespective, below is fixed.
(forAll left : int(1..col-1) .
$ Everything below is fixed
(forall below : GRIDROWS .
(below > fpRow[step]) ->
(grid[step,below,left] = grid[step-1,below,left])
) /\
$ Top row guaranteed to be empty.
(grid[step,1,left] = EMPTY) /\
$ Otherwise fall from above.
((fpRow[step] > 1) ->
(forAll above : int(2..gridHeight) .
above <= fpRow[step] ->
grid[step,above,left] = grid[step-1,above-1,left]
)
)
) /\
$ this col: all fixed apart from fprow, which exchanges with the hand
(hand[step] = grid[step-1, fpRow[step], col]) /\
(grid[step, fpRow[step], col] = hand[step-1]) /\
(forAll colBlock : GRIDROWS .
(colBlock != fpRow[step]) ->
(grid[step,colBlock,col] = grid[step-1,colBlock,col])
) /\
$ right: all fixed
(forAll right : int(col+1..gridWidth) .
forAll colBlock : GRIDROWS .
grid[step,colBlock,right] = grid[step-1,colBlock,right]
)
)
)
\/
$ Wall and fall shot, case 1: no block of colour != hand
(
$ Preconditions:
(
$ This row is a mix of hand and empty.
(forAll col : GRIDCOLS .
(grid[step-1,fpRow[step], col] = EMPTY) \/
(grid[step-1,fpRow[step], col] = hand[step-1])
) /\
$ last col from next row down is a mix of hand and empty.
(forAll row : GRIDROWS .
(row > fpRow[step])
->
((grid[step-1,row,gridWidth] = EMPTY) \/
(grid[step-1,row,gridWidth] = hand[step-1]))
) /\
$ But there must be a hand-coloured block somewhere
((exists col : GRIDCOLS . grid[step-1,fpRow[step],col] = hand[step-1])
\/
(exists row : GRIDROWS .
(row > fpRow[step]) /\
(grid[step-1,row,gridWidth] = hand[step-1])
)
)
)
/\
$ Effects
(
$ hand is unchanged.
(hand[step] = hand[step-1]) /\
$ left: Blocks falling, staying fixed.
$ NB If nothing above then replacing with up still works: it is EMPTY
$ So split is on fpRow[step] > 1. Irrespective, below is fixed.
(forAll left : int(1..gridWidth-1) .
$ Everything below is fixed
(forall below : GRIDROWS .
(below > fpRow[step]) ->
(grid[step,below,left] = grid[step-1,below,left])
) /\
$ Top row guaranteed to be empty.
(grid[step,1,left] = EMPTY) /\
$ Otherwise fall from above.
((fpRow[step] > 1) ->
(forAll above : int(2..gridHeight) .
above <= fpRow[step] ->
grid[step,above,left] = grid[step-1,above-1,left]
)
)
) /\
$ Last column. Everything remaining falls gridHeight - fpRow[step] + 1
(
$ Move the fallen material
(forAll row : GRIDROWS .
row > gridHeight - fpRow[step] + 1
->
grid[step,row,gridWidth] = grid[step-1,row-(gridHeight - fpRow[step] + 1),gridWidth]
) /\
$ Everything above the fallen material is empty.
(forAll row : GRIDROWS .
row <= gridHeight - fpRow[step] + 1
->
grid[step,row,gridWidth] = EMPTY)
)
)
)
\/
$ Wall and fall shot, case 2: a block of colour != hand in the final col
(exists row : GRIDROWS .
$ Preconditions:
( $ block different from hand in final col, below fpRow.
(row > fpRow[step]) /\
(grid[step-1,row,gridWidth] != hand[step-1]) /\
$ fpRow is all hand/empty as is final col from fpRow to row above this.
(forAll col : GRIDCOLS .
(grid[step-1,fpRow[step],col] = hand[step-1]) \/
(grid[step-1,fpRow[step],col] = EMPTY)
) /\
(forAll row2 : int(1..row-1) .
row2 > fpRow[step] ->
((grid[step-1,row2,gridWidth] = hand[step-1]) \/
(grid[step-1,row2,gridWidth] = EMPTY))
) /\
$ But there must exist a block of hand colour somewhere
((exists col : GRIDCOLS .
grid[step-1,fpRow[step],col] = hand[step-1]) \/
(exists row2 : int(1..row-1).
row2 > fpRow[step] /\
grid[step-1,row2,gridWidth] = hand[step-1]))
)
/\
$ Effects:
( $ hand exchanges with the block identified in final col.
(hand[step] = grid[step-1,row,gridWidth]) /\
(grid[step,row,gridWidth] = hand[step-1]) /\
$ Everything below in the final column is fixed.
(forAll below : int(row+1..gridHeight) .
grid[step,below,gridWidth] = grid[step-1,below,gridWidth]
) /\
$ Blocks above block identified in the final col fall (row - fpRow) cells.
($ Which means the top row - fpRow cells must be empty:
(forAll row2 : GRIDROWS .
row2 <= row - fpRow[step]
->
grid[step,row2,gridWidth] = EMPTY) /\
$ And the cells from there down to one above the identified block are
$ determined by what was row-fpRow[step] above them at the previous step.
(forAll row2 : GRIDROWS .
(row2 > row - fpRow[step]) /\ (row2 < row)
->
grid[step,row2,gridWidth] =
grid[step-1,row2 - (row -fpRow[step]),gridWidth]
)
) /\
$ Everything left falls or stays fixed.
(forAll left : int(1..gridWidth-1) .
$ Everything below is fixed
(forall below : GRIDROWS .
(below > fpRow[step]) ->
(grid[step,below,left] = grid[step-1,below,left])
) /\
$ Top row guaranteed to be empty.
(grid[step,1,left] = EMPTY) /\
$ Otherwise fall from above.
((fpRow[step] > 1) ->
(forAll above : int(2..gridHeight) .
above <= fpRow[step] ->
grid[step,above,left] = grid[step-1,above-1,left]
)
)
)
)
),
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
true