The goal of the game is to flip 3*3 blocks of dots (o
) and dashes (-
) until the board contains exactly one dot, in the middle (if you try an even board size (see below), you can see where the target board’s "middle" dot should be by loading this file in GHCi and running putStrLn $ o 0 t
).
You pick a block by typing its central coordinates in the format <column letter><row number><enter>
.
E.g. given the following 5*5 board:
-----
-ooo- 1
-o-o- 2
-ooo- 3
-----
abc
you could win the game in one move by flipping the middle block. To do this,
you’d type b2
and <enter>
, giving:
-----
----- 1
--o-- 2
----- 3
-----
abc
Be careful when you type; the game will print "X"
and terminate if you type invalid input (sorry!).
Backspace works though, so you can at least change what you’ve typed before you press <enter>
.
The numbers displayed at the top of the screen are the number of moves you’ve made and the minimum number of moves required to win the game. Finishing in any number of moves is worthy of celebration, but for large boards, finishing in the minimum number of moves is an achievement that you can be proud of!
You can change the board size by changing the i
variable (at the start of line2) to any value in [2..9]
.
This controls the size of the board’s interior.
You can alter the starting board by changing the pseudorandom number generator seed.
This is the second argument of the call to iterate
, at the start of line 4.
I’m pretty sure that you can’t win in fewer moves than the displayed "minimum number of moves", but I haven’t proven this. If anyone thinks of a proof, or finds a counterargument, do let me know! FYI the starting board is generated by making a random decision about whether or not to flip the 3*3 block around each interior point.
I’d also be very interested in knowing if anyone manages to reduce the board to a single dot that is not in the middle. Again, I’m reasonably sure that this is impossible (note that the game will not treat this as a win if it happens), but I haven’t proven it.
This is an alphabetical listing of all of the functions and constants that I wrote:
-
a :: Int → String → String
:-
Applies
f
to theChar
at the given index (i.e. it flips that symbol).
-
-
b :: Int → a → (Int → a) → a
:-
A Branch function.
b z d f
returnsf z
ifz
is in[1..i]
, otherwise it returnsd
.
-
-
c :: [Int]
-
Pseudorandom linearised Coordinates in the board’s interior.
-
-
e :: Int
-
The width/height of the board’s Exterior, i.e.
i+2
.
-
-
f :: Char → Char
-
Flips
o
to-
and vice-versa.
-
-
h :: Int → String → String
-
Flips the neighbourHood of the given linearised point.
-
-
i :: Int
-
The width/height of the board’s Interior. Must be in
[2..9]
.
-
-
j :: Int
-
The number of times the target board is Jumbled by randomly flipping neighbourhoods, in order to produce the start board.
-
-
k :: String
-
The Keys that you should type to pick a column.
-
-
l :: Int → Int → Int
-
(l x y)
Linearises 2D coordinate(x,y)
. I.e. it turns it into an index in[0..e^2-1]
.x
andy
must be in[0..e-1]
.
-
-
m :: Int
-
LCG (linear congruential generator) Modulus.
-
-
n :: Int → [Int]
-
Maps a linearised point to its 3*3 Neighbourhood.
-
-
o :: Int → String → String
-
Formats the given board for Output. The
Int
is the index of theString
's initial row.
-
-
r :: Int → Int
-
Returns the next number in an LCG pseudoRandom sequence.
-
-
s :: String
-
The Start board.
-
-
t :: String
-
The Target board. This is not necessarily the only solution, but it is a known solution.
-
-
u :: Int → String → String → String
-
The User input processing loop.
u moveCount gameBoard input
reads the first two characters and'\n'
character from input, interprets the first two characters as coordinates, flips the neighbourhood of the coordinates and loops until there’s one dot left, or until you type invalid input.
-
-
v :: Char → Int
-
The ASCII Value of a Char.
-