Programming the fiver game
In this case I don't know how to post something helpful without providing full code so I'll just do that and hope this wasn't homework. My emphasis is on clarity (hopefully) rather than brevity or peak efficiency.
flip = # /. {LightRed -> LightBlue, LightBlue -> LightRed} &;
flipNeighbors[i_, j_] :=
(color[##] = flip @ color[##];) & @@@
{{i, j}, {i + 1, j}, {i - 1, j}, {i, j + 1}, {i, j - 1}}
ClearAll[color]
color[_, _] = LightBlue;
Grid[
Array[
Button[
Spacer[{50, 50}]
, flipNeighbors[##]
, Background -> Dynamic @ color[##]
] &
, {5, 5}
]
, Spacings -> {-0.03, -0.01}
]
Notes
- Negative
Spacings
values are used to snug up the buttons. ClearAll[color]; color[_, _] = LightBlue;
should be evaluated to reset the board.DynamicModule
should be used, with localization forflip
,flipNeighbors
, andcolor
, if you want the game to appear correctly when you first open a Notebook containing it.Appearance -> None
may be used as a Option forButton
if you do not like the "3D" border.- I made the value of the Button
Background
Dynamic, rather than the entireGrid
, for improved performance.
The full monty
This is a fun little game so I wrote code for my own reuse. I might as well share it. :-)
The output may be copied and used independently, with controls to reset the board and change the colors when your eyes get tired.
DynamicModule[{flip, v, c, square, gui},
flip[i_, j_] :=
(v[##] *= -1;) & @@@
{{i, j}, {i + 1, j}, {i - 1, j}, {i, j + 1}, {i, j - 1}};
_v = 1;
{c[1], c[-1], c[0]} = {LightBlue, LightRed, Gray};
square =
Button[
Spacer[{51, 51}], flip @ ##
, Background -> Dynamic @ c @ v @ ##
, Appearance -> None
] &;
gui =
Labeled[ #
, {Button["Reset", ClearAll[v]; _v = 1],
ColorSetter@*Dynamic@*c /@ {1, -1, 0} // Column}
, {Bottom, Right}
] &;
Grid[
Array[square, {5, 5}]
, Frame -> c[0]
, Background -> c[0]
, Spacings -> {{5, {1}, 5}, {5, {1}, 5}}/10
] // gui // Deploy
]
Here is a version that uses only the Front End, which can be verified using LinkSnooper. This is mostly a useless optimisation/exploration.
width = 3;
height = 3;
n = width*height;
heldStates =
Join @@ (ToExpression["state" <> ToString[#], InputForm, Hold] & /@
Range[n]);
groupedEdges =
Flatten[#, 1] &@
Table[Block[
{sqs = {0}, index = xii + width (yjj - 1)}
,
If[xii != width, AppendTo[sqs, 1]];
If[xii != 1, AppendTo[sqs, -1]];
If[yjj != height, AppendTo[sqs, width]];
If[yjj != 1, AppendTo[sqs, -width]];
index + # & /@ sqs]
,
{yjj, 1, height},
{xii, 1, width}
];
heldConnectedStates = heldStates[[#]] & /@ groupedEdges;
dynModVars =
List @@@
Hold@Evaluate[
Set @@@ Thread[{heldStates, Hold @@ ConstantArray[False, n]},
Hold]];
Heres the graphical stuff
interactiveRectangleMaker =
Function[{sA, boxDirective, action},
{DynamicBox[If[sA, RGBColor[1, 0, 1], RGBColor[0, 1, 0]]],
EventHandler[boxDirective, {"MouseDown" :> action}]}, HoldAll];
actionsHeld =
Join @@ (CompoundExpression @@@ Hold[Evaluate[
Join @@
Function[Null, Hold[FEPrivate`Set[#, SameQ[#, False]]],
HoldAll] /@ #
]] & /@ heldConnectedStates);
doubleRange = Transpose@Outer[List, Range[width], Range[height]];
rectBoxes = Flatten@Map[RectangleBox, doubleRange, {2}];
argsHeld =
Thread[{heldStates, Hold @@ rectBoxes, actionsHeld }, Hold];
This makes the DynamicModule
DynamicModule @@
{Unevaluated @@ dynModVars,
Graphics[
List @@ interactiveRectangleMaker @@@ argsHeld
,
GridLines -> {Range[2, width], Range[2, height]}
]}