4
$\begingroup$

I'm trying to build a tic tac toe program using a 3x3 board list running inside of a DynamicModule with a separate window displaying the current value and accepting clicks from the user. For now I'm just trying to use EventHandler to set any empty boxes to X's when clicked.

When I click on a box, I get the error

Part::pkspec1: The expression x cannot be used as a part specification.

Is this an issue with assigning a new value for board inside of the EventHandler?

How else should I structure this?

createBox[elem_] := Show[
  Graphics[{White, Rectangle[]}, Frame -> True, 
   FrameStyle -> Thickness[.02], FrameTicks -> None],
  Graphics@Text[Style[elem, FontSize -> 100], {.5, .5}],
  ImageSize -> 150
  ]  

DynamicModule[{board = Table[Table[" ", 3], 3]},
  CreateWindow[
   DocumentNotebook[
    (*Displaying grid*)
    Dynamic@Grid[
      Table[Table[
        EventHandler[
         createBox[board[[y]][[x]]]
         , {"MouseClicked" :> (
            If[board[[y]][[x]] == " ", board[[y]][[x]] = "X";];
            )}]
        , {x, 3}], {y, 3}],
      Spacings -> {0, 0}]
    ],
   WindowElements -> {}, WindowTitle -> "Tic Tac Toe", 
   WindowSize -> All, Editable -> False, Selectable -> False, 
   ShowCellBracket -> False, WindowFrameElements -> {"CloseBox"}];
  ];
$\endgroup$
  • $\begingroup$ In the "Applications" section of Toggler there's an example of one way to use it for a Tic-Tac-Toe game (might be a possible starting point). $\endgroup$ – Anne Dec 13 '17 at 6:13
  • $\begingroup$ @Anne I'm not quite sure if this is what I'm looking for as I'm also aiming so have an AI player later on which won't be able to click the squares (I'd also prefer to have my own function to make moves rather than toggling a variable, my code is just testing click functionality) $\endgroup$ – Chris Dec 13 '17 at 13:22
  • $\begingroup$ Yeah, that makes total sense (wasn't expecting it to fit exactly, just thought I'd throw it out there just in case). $\endgroup$ – Anne Dec 13 '17 at 15:17
1
$\begingroup$

Main issues:

  • set part

    board[[y]][[x]] = val will not work, you need board[[y, x]] = "X"

    See: Part not behaving the way I expect it to

  • x and y can't get inside :>

    RuleDelayed holds and so after Table is done you will have board[[y, x]] in each item, instead of expected e.g. board[[2,1]] etc.

    The way to go is to use With[{i=i},... idiom, see: Function in Table

  • DynamicModule is 'a scoped rectangle in your screen'

    So instead of DynamicModule[.. CreateWindow.. it should rather be CreateWindow[.. DynamicModule..

    See e.g. IntroductionToDynamic

Secondary issues

  • you don't need Dynamic around Grid, only content of board changes so you can put it inside createBox[Dynamic[...]]

  • it is better to use === instead of == to compare non numerical values

  • Show[Graphics...] can be combined in one Graphics[...]

  • Unless you need it we can replace CreateWindow[NotebookDocument with CreateDocument (omg, I just realized the origin of this function name, thanks)

  • but your notebook's options suggest it may be enough to use CreateDialog

  • you can combine nested Table into one

Summing up:

createBox[elem_] := Graphics[
  { { White, Rectangle[] }
  , Text[Style[elem, FontSize -> 100], {.5, .5}]
  }
, ImageSize -> 150
, Frame -> True
, FrameStyle -> Thickness[.02]
, FrameTicks -> None
];

CreateDialog[
  DynamicModule[
    {board = ConstantArray[" ", {3, 3}]}
  , Grid[
      Table[ With[{x = x, y = y}
      , EventHandler[
          createBox[ Dynamic @ board[[y, x]]]
        , {"MouseClicked" :> If[board[[y, x]] === " ", board[[y, x]] = "X"]
          }
        ]]
      , {x, 3}, {y, 3}
      ]
    , Spacings -> {0, 0}
    ]
  ]
, WindowTitle -> "Tic Tac Toe"
, WindowSize -> All
];

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.