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}
]

enter image description here

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 for flip, flipNeighbors, and color, if you want the game to appear correctly when you first open a Notebook containing it.
  • Appearance -> None may be used as a Option for Button if you do not like the "3D" border.
  • I made the value of the Button Background Dynamic, rather than the entire Grid, 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
]

enter image description here


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]}
   ]}