Mathematica Stack Exchange is a question and answer site for users of Mathematica. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

This is a followup to my previous question: How to simplify block of multiple repetitive If statements?

I have another block of If statements, somewhat like the other one I need help simplifying (making pretty?)

In my game, the players can only make moves on odd rows and columns of the board. The even spaces are used for book-keeping of connections, to avoid ambiguous crosscut situations.

Anyway, the code I have written is below. The algorithm is:

1) If the clicked row and column is empty, mark it as the current player's.

2) For each of the eight directions around the current space:

3) If there is a space 2 steps in that direction that belongs to the current player, and the even space between it and the clicked space is empty, mark it as the current player's too (make a bridge.)

Variables:

Board - matrix used to hold the board state

Gridsize - size of the matrix used to store the board state

MoveC - column of the clicked move; odd number between 1 and the GridSize of the current board

MoveR - row of the clicked move; odd number between 1 and the GridSize of the current board

ActivePlayer - holds the current active player's handle

InactivePlayer - holds the other player's handle

The less than/greater than statements are to check for edge cases. As far as I can tell, this code does what I want, but every time I look at it, my eyes bleed. Surely theres a better implementation?

 If[Board[[MoveR, MoveC]] == 0, Board[[MoveR, MoveC]] = ActivePlayer;                       

     If[MoveC > 1 && Board[[MoveR, MoveC - 2]] == ActivePlayer, Board[[MoveR, MoveC - 1]] = ActivePlayer];

     If[MoveC < (GridSize - 1) && Board[[MoveR, MoveC + 2]] == ActivePlayer, Board[[MoveR, MoveC + 1]] = ActivePlayer];

     If[MoveR > 1 && Board[[MoveR - 2, MoveC]] == ActivePlayer, Board[[MoveR - 1, MoveC]] = ActivePlayer];

     If[MoveR < (GridSize - 1) && Board[[MoveR + 2, MoveC]] == ActivePlayer, Board[[MoveR + 1, MoveC]] = ActivePlayer];

     If[MoveR > 1 && MoveC > 1 && Board[[MoveR - 2, MoveC - 2]] == ActivePlayer && Board[[MoveR - 1, MoveC - 1]] == 0, Board[[MoveR - 1, MoveC - 1]] = ActivePlayer];

     If[MoveR < (GridSize - 1) && MoveC > 1 && Board[[MoveR + 2, MoveC - 2]] == ActivePlayer && Board[[MoveR + 1, MoveC - 1]] == 0, Board[[MoveR + 1, MoveC - 1]] = ActivePlayer];

     If[MoveR > 1 && MoveC < (GridSize - 1) && Board[[MoveR - 2, MoveC + 2]] == ActivePlayer && Board[[MoveR - 1, MoveC + 1]] == 0, Board[[MoveR - 1, MoveC + 1]] = ActivePlayer];

     If[MoveR < (GridSize - 1) && MoveC < (GridSize - 1) && Board[[MoveR + 2, MoveC + 2]] == ActivePlayer && Board[[MoveR + 1, MoveC + 1]] == 0, Board[[MoveR + 1, MoveC + 1]] = ActivePlayer];

 ]

Each of the Ifs after the first one do pretty much the same thing, just in a different direction. Much thanks for any help or ideas.....!!!!

share|improve this question
up vote 5 down vote accepted

Here's a very quick-and-dirty, call with player ID, the array, and the move position. Assumes you call with valid move (e.g., not checking for odd row/col or space empty - do before call or add...), it returns the new array with play position and any intervening positions that meet the conditions filled in with player id:

doit[player_, grid_, {posx_, posy_}] := 
 Module[{gridtmp = ArrayPad[grid, {2, 2}], wrk, ds},
  wrk = gridtmp[[posx ;; posx + 4, posy ;; posy + 4]];
  ds = Downsample[wrk, 2];
  gridtmp[[posx ;; posx + 4, posy ;; posy + 4]] = 
   ReplacePart[wrk, {x_ /; Abs[3 - x] <= 1, y_ /; Abs[3 - y] <= 1} /; 
               wrk[[x, y]] == 0 && EvenQ@x && EvenQ@y &&
               ds[[x - 1, y - 1]] == player -> player];
  gridtmp[[posx + 2, posy + 2]] = player;
  ArrayPad[gridtmp, {-2, -2}]];

Example usage:

myNewGrid=doit[a,myCurrentGrid,{5,3}]

I highly recommend going through the documentation sections for new users, like "Intro for programmers", and the top FAQ question here, get used to doing things functionally and using MMA style - in general, doing things imperatively and destructively (changing the array directly vs returning a new changed copy) is non-productive and makes code harder to understand.

Edit: Here's what this is doing.

The core is getting the "neighborhoods" of the spot, which is a bit of a PITA (though less so in 10.x with some of the new functions, but since you did not specify version, I used 9.x compatibility as a yardstick).

Rather than go through those neighborhood machination, it's easier to temporarily make the array bigger, making grabbing the 5 x 5 outer neighborhood a trivial Part operation. That's what is done in the ArrayPad into gridtmp.

Next, we put the 5 x 5 chunk into wrk.

We then Downsample wrk into ds: this gives use the 2-away neighbors from the play position that lie opposite the 8 immediate neighbors.

We then take the 5 x 5 chunk through a ReplacePart, checking within that a spot is 1) free and an immediate neighbor of the play spot and 2) the 2-away neighbor opposite the immediate neighbor is owned by the player.

If the conditions are met, the immediate neighbor is replaced with the player id.

The completed 5 x 5 chunk is replaced into the temporarily expanded grid, the play spot is updated, and finally we trim the expansion back off the array, returning the changed and original dimension array.

N.B.: This demonstrates a generalized solution, that is, it can easily be changed for differing rules/neighborhood size etc. It's probably a bit of overkill for your specific need, since in your case, there are only 4 positions that can possibly change (the "corner neighbors"), so you could simplify your If stuff greatly.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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