Group Abstract Group Abstract

Message Boards Message Boards

Taking chess to 2.5 dimensions

Posted 2 years ago

In a previous thread we found that it is not too difficult, but also not very easy to Classify parts of 2D SNES maps in terms of their heights. What's much easier is to simply make our own voxelized height map, and use such a geometry as the scene of game involving voxel chess pieces. The purpose of this memo is to build on Keying's initial game design study by showing how to automate camera and character control by key pressing.

First we need to import pieces:

ToCubes[verts_, cols_] := With[
  {colRep = Rule[#[[1]], RGBColor[#[[2 ;; 4]]/255]] & /@ cols},
  {#[[4]] /. colRep, 
     Cuboid[(#[[1 ;; 3]] + {4, 4, 0})/8, (#[[1 ;; 3]] + {5, 5, 1})/
       8]} & /@ verts]

ImportVox[file_] := With[
  {lines = StringSplit[Import[file], "\n"]}, Function[{markers},
    ToCubes[Map[ToExpression, DeleteCases[
       StringSplit[lines[[markers[[2]] + 1 ;; markers[[3]] - 1]], 
        " "], "", Infinity], {2}],
     Map[ToExpression, 
      DeleteCases[StringSplit[lines[[markers[[4]] + 1 ;; -1]],
        " "], "", Infinity], {2}]]][
   Flatten[Position[lines, "---------------------"]]]]

AbsoluteTiming[
 primitives =  MapApply[RegionUnion, Map[Last, SortBy[GatherBy[ImportVox[
"https://raw.githubusercontent.com/bradklee/OpenAssets/main/WorldChess/TXT/Y" <> # <> "Vox.txt"
          ], First], Length], {2}]] & /@ {"Pawn", "Rook", "Knight", "Bishop", "Queen", "King"};]

Graphics3D/@primitives

chess pieces

(perhaps we should just make a WFR for these?)

Next we generate the setting:

HeightRules[mazeData_] := Module[{
   g1 = NearestNeighborGraph[Position[mazeData, 5]],
   wallComponents = WeaklyConnectedComponents[
     NearestNeighborGraph[Position[mazeData, 0],
      {4, 1}]], heightRulesLow, heightRulesHigh},
  heightRulesLow = Map[Function[{vertex},
     vertex -> Plus[1, Floor[Min[Catenate[Outer[
             GraphDistance[g1, {#1, #2}, vertex] &,
             MinMax[VertexList[g1]],
             MinMax[VertexList[g1]]]]]/6]/2]  ],
    VertexList[g1]];
  heightRulesHigh = MapThread[Rule, {Catenate[wallComponents],
     M &, Catenate[Table[First[First[
            SortBy[Tally[DeleteCases[ReplaceAll[
                Union[Catenate[Outer[Plus, #,
                   CirclePoints[{1, 0}, 4], 1]]],
                heightRulesLow], _List]], Last]]],
          Length[#]] & /@ wallComponents]]}];
  {heightRulesLow, heightRulesHigh}
  ]

With[{heightRules = CompoundExpression[SeedRandom[21234312],
    HeightRules[ResourceFunction["RandomSierpinskiMaze"][2][[1, 1]]]
    ]},
 voxelSet = Graphics3D[Transpose[{{Lighter@Gray, Gray},
     Map[Cuboid[# {1, 1, 0}, # + {1, 1, 0}] &,
        MapApply[Append, #]] & /@ heightRules}],
   ViewPoint -> {Infinity, -Infinity, Infinity},
   ViewVertical -> {0, 0, 1},
   Boxed -> False];
 moveGraph = With[{g1 = NearestNeighborGraph[
      MapApply[Append, Catenate[heightRules]],
      {4, Sqrt[2]*.9}]}, Graph3D[g1,
    VertexCoordinates -> Map[# -> (# + {1/2, 1/2, 1/4}) &,
      VertexList[g1]],
    VertexStyle -> Black, EdgeStyle -> Black]];
 ]

(This could really be any height map you want, even one ripped from a console game).

Finally, we introduce a few functions for moving pieces around, and plot the whole thing using a DynamicModule with EventHandler for key logging:

PlacePiece[primitives_][
  index_, angle_, pos_, col_ : {Yellow, Orange}
  ] := Graphics3D[{EdgeForm[None],
   Riffle[col,
    Translate[Rotate[
        #, angle, {0, 0, 1}, {1/2, 1/2, 0}
        ], pos] & /@ primitives[[index]]]}
  ]

MovePiece[moveGraph_][pos_, step_] := With[
  {next = SelectFirst[
     VertexOutComponent[moveGraph, pos, {1}],
     #[[1 ;; 2]] == Plus[pos, step][[1 ;; 2]] &, True]},
  If[TrueQ[next], pos, next]
  ]

DynamicModule[{
  views = Catenate[Outer[
      {#1 Infinity, #2 Infinity, Infinity} &,
      {1, -1}, {1, -1}]][[{1, 2, 4, 3}]],
  verticals = Catenate[Outer[
      {If[#1 , #2, 0], If[#1 , 0, #2], 0} &,
      {True, False}, {-1, 1}]][[{3, 1, 4, 2}]],
  allpos = If[False, {
     {3, 3, 1}, {4, 3, 1}, {5, 3, 1}, {6, 3, 1},
     {3, 2, 1}, {4, 2, 1}, {5, 2, 1}, {6, 2, 1},
     {29, 3, 1}, {29, 4, 1}, {29, 5, 1}, {29, 6, 1},
     {30, 3, 1}, {30, 4, 1}, {30, 5, 1}, {30, 6, 1},
     {3, 29, 1}, {4, 29, 1}, {5, 29, 1}, {6, 29, 1},
     {3, 30, 1}, {4, 30, 1}, {5, 30, 1}, {6, 30, 1},
     {29, 29, 1}, {28, 29, 1}, {27, 29, 1}, {26, 29, 1},
     {29, 30, 1}, {28, 30, 1}, {27, 30, 1}, {26, 30, 1}
     }, RandomSample[VertexList[moveGraph], 32]],
  allface = Join[
    ConstantArray[2, 8],
    ConstantArray[1, 8],
    ConstantArray[4, 16]
    ],
  allind = {
    1, 1, 1, 1, 2, 3, 4, 5,
    1, 1, 1, 1, 6, 4, 3, 2,
    1, 1, 1, 1, 2, 3, 4, 5,
    1, 1, 1, 1, 2, 3, 4, 6
    },
  allcolor = Join[
    ConstantArray[1, 16],
    ConstantArray[2, 16]
    ],
  who = 1,
  top = False,
  graph = False,
  pos, face, obstructedMoveGraph,
  dir = 0},
 pos = allpos[[who]];
 face = allface[[who]];
 obstructedMoveGraph = 
  VertexDelete[moveGraph, 
   Alternatives @@ Complement[allpos, {allpos[[who]]}]];
 EventHandler[Dynamic@Show[
    voxelSet,
    If[graph, obstructedMoveGraph, Graphics3D[{}]],
    PlacePiece[primitives][allind[[who]], -(allface[[who]] + 1) *Pi/2,
      allpos[[who]],
     Association[{
        1 -> {Red, Lighter[Orange, .5]}, 
        2 -> Lighter@{Red, Lighter[Yellow, .5]}}][allcolor[[who]]]
     ],
    MapThread[
     PlacePiece[primitives][#1, -(#2 + 1) *Pi/2, #3, Association[{
          1 -> {Yellow, Orange}, 2 -> {Orange, Yellow}}][#4]] &,
     {allind, allface, allpos, allcolor}[[All, 
       Complement[Range[32], {who}]]]
     ],
    ViewPoint -> If[top, {0, 0, Infinity}, views[[dir + 1]]],
    ViewVertical -> If[top, verticals[[dir + 1]], {0, 0, 1}],
    PlotRange -> {{0, 33}, {0, 33}, {0, 10}},
    Boxed -> False,
    ImageSize -> {1200, UpTo[800]}
    ], {
   {"KeyDown", "w"} :> (dir = Mod[dir - 1, 4]),
   {"KeyDown", "q"} :> (dir = Mod[dir + 1, 4]),
   {"KeyDown", "t"} :> (top = Not[top]),
   {"KeyDown", "g"} :> (graph = Not[graph]),
   {"KeyDown", "c"} :> (who = Mod[who + 1, 32, 1];
         obstructedMoveGraph = 
          VertexDelete[moveGraph, 
           Alternatives @@ Complement[allpos, {allpos[[who]]}]]),
   {"KeyDown", "x"} :> (who = Mod[who - 1, 32, 1];
     obstructedMoveGraph = 
      VertexDelete[moveGraph, 
       Alternatives @@ Complement[allpos, {allpos[[who]]}]]),
   "UpArrowKeyDown" :> (allpos[[who]] = 
      MovePiece[obstructedMoveGraph][allpos[[who]], 
       verticals[[Mod[allface[[who]] = dir, 4] + 1]]]),
   "RightArrowKeyDown" :> (allpos[[who]] = 
      MovePiece[obstructedMoveGraph][allpos[[who]], 
       verticals[[Mod[allface[[who]] = dir + 1, 4] + 1]]]),
   "DownArrowKeyDown" :> (allpos[[who]] = 
      MovePiece[obstructedMoveGraph][allpos[[who]], 
       verticals[[Mod[allface[[who]] = dir + 2, 4] + 1]]]),
   "LeftArrowKeyDown" :> (allpos[[who]] = 
      MovePiece[obstructedMoveGraph][allpos[[who]], 
       verticals[[Mod[allface[[who]] = dir + 3, 4] + 1]]])
   }]]

frame 1

The controls are as follows. Press "q" (or reverse "w") rotates camera:

frame 2

Press "t" to toggle top down view:

frame 3

(notice pieces can be uniquely id'ed from top down view)

press "g" to toggle states adjacency graph:

frame 4

press "t" to toggle back to isomorphic view:

frame 5

Arrow keys move the current selected piece, shown in lighter colors with red accents, and "c" and "x" cycle the piece selector through 32 alternatives. Here's a good initial condition:

initial condition

Of course, this needs more automation, more flexible movement rules, menus, etc. etc. but I think it's already somewhat playable as is.

POSTED BY: Brad Klee
5 Replies
Posted 2 years ago
POSTED BY: Brad Klee

Hey Brad,

This is a very interesting project. However, when I ran your first code (the one intended to generate the 3D models of the chess pieces), I got this message along with the pieces:

{10.5059, Null}

Then, when I ran your second and last demonstrated code, Wolfram, for many minutes, was giving me a "(Running...)" message. I left the program running overnight, and when I checked on it the following morning, it was still giving me this "running" message. Any idea what might be going on here? (I am using v13, Student Edition)

Thanks,

Eleazar

Posted 2 years ago
POSTED BY: Brad Klee

Thanks, Brad, it's working now! As for your question asking if anyone can come up with anything interesting for your new project, I'm still a student learning even just the mere basics of the WL so I'll just follow this post and see what the other, cleverer than I people come up with. :)

Best regards,

Eleazar

enter image description here -- you have earned Featured Contributor Badge enter image description here Your exceptional post has been selected for our editorial column Staff Picks http://wolfr.am/StaffPicks and Your Profile is now distinguished by a Featured Contributor Badge and is displayed on the Featured Contributor Board. Thank you!

POSTED BY: EDITORIAL BOARD