Difference between revisions of "Tic Tac Toe/Game Logic Two"

From Second Life Wiki
Jump to navigation Jump to search
m (TicTacToeGameLogicTwo moved to Tic Tac Toe/Game Logic Two: CamelCasingIsReallyReallyBad)
(wiki-fu)
Line 1: Line 1:
{{LSL Header}}
{{LSL Header}}
===== Putting in the final touches =====
{{:Tic Tac Toe/navigation}}


Now with our uploader in place, we can add another message to the TouchDisplay script to color them red when we detect three in a row:
Now with our uploader in place, we can add another message to the TouchDisplay script to color them red when we detect three in a row:


<pre>
<lsl>
// Locations of various bits on the texture
// Locations of various bits on the texture
float e_pos_s = 0.75;
float e_pos_s = 0.75;
Line 102: Line 102:
     }
     }
}     
}     
</pre>
</lsl>


We then go into the Controller script and add the logic to detect the game end:
We then go into the Controller script and add the logic to detect the game end:


<pre>
<lsl>
// Message constants
// Message constants
integer MSG_RESET      = 0;
integer MSG_RESET      = 0;
Line 269: Line 269:
     }
     }
}
}
</pre>
</lsl>


Note that we don't have to check all possibilities, but only those which could be made possible by the last move.
Note that we don't have to check all possibilities, but only those which could be made possible by the last move.
Line 280: Line 280:


Have fun!
Have fun!
----
[[TicTacToeVersionControl|prev]] | [[TicTacToeSummary|next]]

Revision as of 22:18, 2 January 2008

Now with our uploader in place, we can add another message to the TouchDisplay script to color them red when we detect three in a row:

<lsl> // Locations of various bits on the texture float e_pos_s = 0.75; float e_pos_t = 0.75; float x_pos_s = 0.75; float x_pos_t = 0.25; float o_pos_s = 0.25; float o_pos_t = 0.25;

// Face on wjich the texture lives integer DISPLAY_FACE = 4; vector RED = <1,0,0>; vector WHITE = <1,1,1>;

// Message constants integer MSG_RESET = 0; integer MSG_TOUCH = 1; integer MSG_SET_X = 2; integer MSG_SET_O = 3; integer MSG_IDLE = 4; integer MSG_IDLE_TOUCH = 5; integer MSG_WIN = 6;

default {

   state_entry()
   {
       // If we are in the the root prim...
       if (llGetLinkNumber() < 2)
       {
           // ... disable ourselves
           llSetScriptState(llGetScriptName(), FALSE);
           llSleep(2);
       }
       
       // Reset color and texture
       llSetColor(WHITE, DISPLAY_FACE);
       llOffsetTexture(e_pos_s, e_pos_t, DISPLAY_FACE);
   }
   touch_start(integer touching_agents)
   {
       while (touching_agents--)
       {
           llMessageLinked(LINK_ROOT, MSG_TOUCH, "", llDetectedKey(touching_agents));
       }   
   }
   
   link_message(integer from, integer msg_id, string str, key id)
   {
       if (msg_id == MSG_SET_X) state x;
       if (msg_id == MSG_SET_O) state o;
       if (msg_id == MSG_IDLE) state idle;
   }

}

state x {

   state_entry()
   {
       llOffsetTexture(x_pos_s, x_pos_t, DISPLAY_FACE);
   }
   link_message(integer from, integer msg_id, string str, key id)
   {
       if (msg_id == MSG_RESET) state default;
       if (msg_id == MSG_IDLE) state idle;
       if (msg_id == MSG_WIN) llSetColor(RED, DISPLAY_FACE);
   }

}

state o {

   state_entry()
   {
       llOffsetTexture(o_pos_s, o_pos_t, DISPLAY_FACE);
   }
   link_message(integer from, integer msg_id, string str, key id)
   {
       if (msg_id == MSG_RESET) state default;
       if (msg_id == MSG_IDLE) state idle;
       if (msg_id == MSG_WIN) llSetColor(RED, DISPLAY_FACE);
   }

}

state idle {

   link_message(integer from, integer msg_id, string str, key id)
   {
       if (msg_id == MSG_RESET) state default;
   }
   touch_start(integer touching_agents)
   {
       llMessageLinked(LINK_ROOT, MSG_IDLE_TOUCH, "", NULL_KEY);
   }

} </lsl>

We then go into the Controller script and add the logic to detect the game end:

<lsl> // Message constants integer MSG_RESET = 0; integer MSG_TOUCH = 1; integer MSG_SET_X = 2; integer MSG_SET_O = 3; integer MSG_IDLE = 4; integer MSG_IDLE_TOUCH = 5; integer MSG_WIN = 6;

// Game timeout integer GAME_TIMEOUT = 20;

// Game state key player_x; key player_o; string turn; string game;

// Helper function to linearize our 3x3 array string at(integer u, integer v) {

   integer p = u*3+v;
   return llGetSubString(game, p, p);

}

// return TRUE if game ends integer game_ends(string player, integer move) {

   game = llInsertString(llDeleteSubString(game, move, move), move, player);
   llSay(0, "game = "+game);
   // Check if this made three in a row
   integer u = move / 3;
   integer v = move % 3;
   integer i;
   integer c;
   
   // check horizontal
   c = 0;
   for (i = 0; i < 3; i++) if (at(i, v) == player) c++;
   if (c == 3)
   {
       // The magic number "2" is needed because the smallest child prim index is 2
       for (i = 0; i < 3; i++) llMessageLinked(2 + 3*i + v, MSG_WIN, "", NULL_KEY);
       return TRUE;
   }
   
   // check vertical
   c = 0;
   for (i = 0; i < 3; i++) if (at(u, i) == player) c++;
   if (c == 3)
   {
       // The magic number "2" is needed because the smallest child prim index is 2
       for (i = 0; i < 3; i++) llMessageLinked(2 + 3*u + i, MSG_WIN, "", NULL_KEY);
       return TRUE;
   }
   
   // check if we are on one diagonal
   c = 0;
   if (u == v) for (i = 0; i < 3; i++) if (at(i, i) == player) c++;
   if (c == 3)
   {
       // The magic number "2" is needed because the smallest child prim index is 2
       for (i = 0; i < 3; i++) llMessageLinked(2 + 4*i, MSG_WIN, "", NULL_KEY);
       return TRUE;
   }
   
   // check if we are on the other diagonal
   c = 0;
   if (u + v == 2) for (i = 0; i < 3; i++) if (at(i, 2-i) == player) c++;
   if (c == 3)
   {
       // Arithmetic has been applied, reconstruction left as an exercise to the reader
       for (i = 0; i < 3; i++) llMessageLinked(4 + 2*i, MSG_WIN, "", NULL_KEY);
       return TRUE;
   }
   
   // Check if there are any spaces left
   if (llSubStringIndex(game, "-") < 0) return TRUE;
   // Game goes on
   return FALSE;

}

default {

   state_entry()
   {
       player_x = NULL_KEY;
       player_o = NULL_KEY;
       game = "---------";
       turn = "x";
       llMessageLinked(LINK_ALL_CHILDREN, MSG_RESET, "", NULL_KEY);
       state playing;
   }

}

state playing {

   link_message(integer from, integer msg_id, string str, key id)
   {
       if (msg_id == MSG_TOUCH)
       {
           llSay(0, "touch from "+(string)from);
           if (turn == "x" && NULL_KEY == player_x)
           {
               llSetTimerEvent(0);
               llMessageLinked(from, MSG_SET_X, "", NULL_KEY);
               player_x = id;
               if (game_ends(turn, from-2)) state idle; // magic "2" = smallest link prim index
               turn = "o";
               llSetTimerEvent(GAME_TIMEOUT);
           }
           else if (turn == "o" && NULL_KEY == player_o)
           {
               llSetTimerEvent(0);
               llMessageLinked(from, MSG_SET_O, "", NULL_KEY);
               player_o = id;
               if (game_ends(turn, from-2)) state idle; // magic "2" = smallest link prim index
               turn = "x";
               llSetTimerEvent(GAME_TIMEOUT);
           }
           else if (turn == "x" && id == player_x)
           {
               llSetTimerEvent(0);
               llMessageLinked(from, MSG_SET_X, "", NULL_KEY);
               if (game_ends(turn, from-2)) state idle; // magic "2" = smallest link prim index
               turn = "o";
               llSetTimerEvent(GAME_TIMEOUT);
           }
           else if (turn == "o" && id == player_o)
           {
               llSetTimerEvent(0);
               llMessageLinked(from, MSG_SET_O, "", NULL_KEY);
               if (game_ends(turn, from-2)) state idle; // magic "2" = smallest link prim index
               turn = "x";
               llSetTimerEvent(GAME_TIMEOUT);
           }
       }
   }
   timer()
   {
       llSetTimerEvent(0);
       state idle;
   }

}

state idle {

   state_entry()
   {
       llSay(0, "state idle");
       llMessageLinked(LINK_ALL_CHILDREN, MSG_IDLE, "", NULL_KEY);
   }
   
   link_message(integer from, integer msg_id, string str, key id)
   {
       if (msg_id == MSG_IDLE_TOUCH) state default;
   }

} </lsl>

Note that we don't have to check all possibilities, but only those which could be made possible by the last move.

We also needed to reorder our handling of incoming touch events slightly to ensure that the event timer is off when we process moves.

Then we type \1 update into the chat and play a couple of games to test.

Done.

Have fun!