Join us in Outworldz at www.outworldz.com:9000 or follow us:

Search dozens of selected web sites for OpenSim and LSL script

New! Script Meta-Search will search thousands of scripts here and at other sites for LSL or Opensim scripts.
Loading

Want to add a script or a project? Upload it and a half million people will see it and your name here this year.

Home   Show All
Category: Contributor: Creator
Games Go_Game  

Go_Game

This is my first (and perhaps only) larg...

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

the Zip file

Download all files for Go_Game
Contents are in zip format, with .LSL (text) source code and LSLEdit (text + Solution) formats.
Get file # 1. Go_Game_1.lsl
Get file # 2. Go_Game_10.lsl
Get file # 3. Go_Game_11.lsl
Get file # 4. Go_Game_2.lsl
Get file # 5. Go_Game_3.lsl
Get file # 6. Go_Game_4.lsl
Get file # 7. Go_Game_5.lsl
Get file # 8. Go_Game_6.lsl
Get file # 9. Go_Game_7.lsl
Get file # 10. Go_Game_8.lsl
Get file # 11. Go_Game_9.lsl
1 // GoButtonScript
2 // Jonathan Shaftoe
3 // A simple button used on the Go board, can be one of Pass, Done, Info, Resign or Reset
4 // Drop into a basic prim, call it GoButton, then store in your inventory for later.
5
6 key gPlayer; // Current player whose turn it is.
7 float gSize; // scaling constant.
8 integer gType; // which type of button we are. 1 - pass, 2 - done, 3 - reset, 4 - resign, 5 - info
9
10 set_size(string str) {
11 gSize = (float)str;
12 llSetScale(<0.2 * gSize, 0.1 * gSize, 0.01 * gSize>);
13 }
14
15 default {
16 state_entry() {
18 PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
19 PRIM_SIZE, <0.2, 0.1, 0.01>,
20 PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>, <0, 0, 0>, 0.0,
21 PRIM_COLOR, ALL_SIDES, <0.8, 0.6, 0.5>, 1.0
22 ]);
24 llSetObjectName("GoButton");
25 }
26
27 on_rez(integer start_param) {
28 if(start_param > 0) {
29 gType = start_param;
30 if(gType == 1) {
31 llSetTouchText("Pass");
32 // llSetText("Pass", <0, 0, 0>, 1.0);
33 llSetObjectDesc("Go Game Pass Button.");
34 llSetTexture("f8919345-d4ab-613f-c594-5cdafcf6f8a1", 0);
35 } else if(gType == 2) {
36 llSetTouchText("Done");
37 // llSetText("Done", <0, 0, 0>, 1.0);
38 llSetObjectDesc("Go Game Done Button.");
39 llSetTexture("9db6cc17-c2bc-04e9-bc5e-659e0b20d82f", 0);
40 } else if(gType == 3) {
41 llSetTouchText("Reset");
42 // llSetText("Reset", <0, 0, 0>, 1.0);
43 llSetObjectDesc("Go Game Reset Button");
44 llSetTexture("619570be-ce9c-11ff-3f8b-9ebab0f3c759", 0);
45 } else if(gType == 4) {
46 llSetTouchText("Resign");
47 // llSetText("Resign", <0, 0, 0>, 1.0);
48 llSetObjectDesc("Go Game Resign Button");
49 llSetTexture("5ad73a1c-060c-3fba-5662-584ddfcdc115", 0);
50 } else if(gType == 5) {
51 llSetTouchText("Info");
52 // llSetText("Info", <0, 0, 0>, 1.0);
53 llSetObjectDesc("Go Game Info Button");
54 llSetTexture("d0b9c9de-a289-01df-734a-10a8201841ad", 0);
55 }
56 }
57 }
58
59 changed(integer change) {
60 if(change & CHANGED_LINK)
61 if(llGetLinkNumber() == 0)
62 llDie(); // so i die
63 }
64
65 touch_start(integer num) {
66 if(gType == 1) {
67 if(llDetectedKey(0) == gPlayer) {
68 llMessageLinked(LINK_ROOT, 10, "", "");
69 }
70 } else if(gType == 2) {
71 if(llDetectedKey(0) == gPlayer) {
72 llMessageLinked(LINK_ROOT, 11, "", "");
73 }
74 } else if(gType == 3) {
76 } else if(gType == 4) {
78 } else if(gType == 5) {
80 }
81 }
82
83 link_message(integer sender, integer num, string str, key id) {
84 if(num == 1) {
85 set_size(str);
86 } else if(num == 0) {
87 gPlayer = id;
88 }
89 }
90 }

Go_Game

Next up, GoJoinButtonScript

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoJoinButtonScript
2 // Jonathan Shaftoe
3 // A simple button used for players to join the Go game, and also to start the game.
4 // Gets disabled and hidden once game is in progress.
5 // Drop into a simple prim, call it GoJoinButton, then store in your inventory for later.
6
7 integer gColour; // what colour are we, 1 - black, 2 - white
8 float gSize; // scaling constant
9 string gPlayAs; // Play as Black or Play as White
10 vector gPlayAsCol; // Actual colour, black or white.
11
12 set_size() {
13 llSetScale(<0.2 * gSize, 0.2 * gSize, 0.1 * gSize>);
14 }
15
16 default {
17 state_entry() {
19 PRIM_TYPE, PRIM_TYPE_SPHERE, PRIM_HOLE_DEFAULT, <0, 0.5, 0>, 0.0, ZERO_VECTOR, <0, 1, 0>,
20 PRIM_SIZE, <0.4, 0.4, 0.1>,
21 PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>, <0, 0, 0>, 0.0,
22 PRIM_COLOR, ALL_SIDES, <0.5, 0.5, 0.5>, 1.0
23 ]);
24 llSetTouchText("Join Game");
25 llSetObjectDesc("Go Game Join Button.");
26 llSetObjectName("GoJoinButton");
28 }
29
30 on_rez(integer start_param) {
31 if(start_param > 0) {
32 gColour = start_param;
33 if(gColour == 1) {
34 llSetColor(<0, 0, 0>, ALL_SIDES);
35 gPlayAs = "Play as Black";
36 gPlayAsCol = <0, 0, 0>;
37 } else if(gColour == 2) {
38 llSetColor(<1, 1, 1>, ALL_SIDES);
39 gPlayAs = "Play as White";
40 gPlayAsCol = <1, 1, 1>;
41 }
42 }
43 }
44
45 link_message(integer sender, integer num, string str, key id) {
46 if(num == 1) {
47 gSize = (float)str;
48 set_size();
49 state enabled;
50 }
51 }
52
53 changed(integer change) {
54 if(change & CHANGED_LINK)
55 if(llGetLinkNumber() == 0)
56 llDie(); // so i die
57 }
58 }
59
60 state enabled {
61 state_entry() {
63 llSetText(gPlayAs, gPlayAsCol, 1.0);
64 set_size();
65 }
66
67 touch_start(integer num) {
68 llMessageLinked(LINK_ROOT, gColour, "", llDetectedKey(0));
69 state disabled;
70 }
71
72 changed(integer change) {
73 if(change & CHANGED_LINK)
74 if(llGetLinkNumber() == 0)
75 llDie(); // so i die
76 }
77 }
78
79 state disabled {
80 state_entry() {
82 llSetScale(<0.01, 0.01, 0.01>);
83 llSetText("", <0, 0, 0>, 1.0);
84 }
85
86 link_message(integer sender, integer num, string str, key id) {
87 if(num == 999) {
88 state enabled;
89 }
90 }
91
92 changed(integer change) {
93 if(change & CHANGED_LINK)
94 if(llGetLinkNumber() == 0)
95 llDie(); // so i die
96 }
97 }

Go_Game

That's the simple stuff done. Now things get more complicated, you need to create two grids, one of 'tiles' (prim textures to display the pieces on the board), and one of 'sensors' (to detect clicks when specifying where you want to play).

First off sensors, here's the GoSensorScript

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoSensorScript
2 // Jonathan Shaftoe
3 // Script for each individual Go Sensor, used to detect mouse clicks to allow the player to select
4 // where they want to play. Uses a two click mechanism to reduce prim usage, a grid of these sensors
5 // moves over the board. For example, using a 19x19 board, 20 sensors in a 4x5 grid are used to allow
6 // the player to select the square they want to play in. To start with, the 4x5 grid covers the
7 // whole board, each sensor covering 20 squares. When one is clicked on, the sensors rearrange to
8 // just be over the 20 squares that were under the sensor clicked, then allowing the player to
9 // select an individual square. It's hard to explain, but when you see it working it'll be much
10 // more sense.
11 // Create a single prim, call it GoSensor, then drop this script in. Then see GoSensorGridScript.
12
13 integer gIndex; // which sensor in the grid am I
14 float gSize; // global scaling factor
15
16 integer gXSensors; // how many sensors across in grid to cover board
17 integer gYSensors; // how many sensors up/down in grid to cover board
18
19 integer gXSquares; // how many squares on the board across covered by a sensor
20 integer gYSquares; // how many squares on the board up/down covered by a sensor
21
22 integer gGameSize; // size of game board, will be 9, 13 or 19
23
24 integer gXPosBase; // x coordinate of sensor in grid
25 integer gYPosBase; // y coordinate of sensor in grid
26
27 integer gXPos; // x position of sensor in grid
28 integer gYPos; // y position of sensor in grid
29
30 integer gZoomPosX; // x coordinate when zoomed in (after first click by player)
31 integer gZoomPosY; // y coordinate when zoomed in
32
33 float gSquareSize; // Size of an individual square on the board.
34
35 key gPlayer; // player whose turn it currently is.
36
37 hide() {
38 llSetScale(<0.01, 0.01, 0.01>);
39 llSetPos(<0, 0, - 0.01>);
40 }
41
42 default {
43 state_entry() {
45 PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
46 PRIM_SIZE, <0.5, 0.5, 0.5>,
47 PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>, <0, 0, 0>, 0.0,
48 PRIM_COLOR, ALL_SIDES, <0, 0, 1>, 0.25
49 ]);
50 llSetObjectName("GoSensor");
51 llSetTouchText("Play here");
52 llSetObjectDesc("Go Game sensor - click once to select region to play in, then again for exact position.");
54 }
55
56 on_rez(integer start_param) {
57 gIndex = start_param;
58 state ready;
59 }
60
61 changed(integer change) {
62 if(change & CHANGED_LINK)
63 if(llGetLinkNumber() == 0)
64 llDie(); // so i die
65 }
66 }
67
68 // Do setup, hide sensor and wait for message
69 state ready {
70 state_entry() {
71 }
72
73 link_message(integer sender, integer num, string str, key id) {
74 if(num == 3) {
75 hide();
76 list params = llParseString2List(str, [","], []);
77 gGameSize = llList2Integer(params, 0);
78 gXSensors = llList2Integer(params, 1);
79 gYSensors = llList2Integer(params, 2);
80 gSize = llList2Float(params, 3);
81
82 gXSquares = llCeil((float)gGameSize / gXSensors);
83 gYSquares = llCeil((float)gGameSize / gYSensors);
84
85 gXPosBase = (gIndex % gXSensors);
86 gYPosBase = (gIndex / gXSensors);
87
88 if((gXPosBase + gYPosBase) % 2 == 0) { // Alternating colours for checkerboard effect
89 llSetColor(<1, 0, 0>, ALL_SIDES);
90 } else {
91 llSetColor(<0, 1, 0>, ALL_SIDES);
92 }
93
94 gXPos = gXPosBase * gXSquares;
95 gYPos = gYPosBase * gYSquares;
96
97 if(gGameSize - gXPos < gXSquares) {
98 gXSquares = gGameSize - gXPos;
99 }
100 if(gGameSize - gYPos < gYSquares) {
101 gYSquares = gGameSize - gYPos;
102 }
103 gSquareSize = gSize / gGameSize;
104 } else if(num == 0) {
105 gPlayer = id;
106 state zoom1;
107 }
108 }
109
110 changed(integer change) {
111 if(change & CHANGED_LINK)
112 if(llGetLinkNumber() == 0)
113 llDie(); // so i die
114 }
115 }
116
117
118 // Move to first position, sensor grid covering whole board, wait for player click
119 state zoom1 {
120 state_entry() {
121 if(gXPosBase >= gXSensors || gYPosBase >= gYSensors) {
122 hide();
123 } else {
124 float width = gSquareSize * gXSquares;
125 float height = gSquareSize * gYSquares;
126 llSetScale(<width, height, 0.01 * gSize>);
127 llSetPos(<(gXPos + (gXSquares / 2.0)) * gSquareSize - gSize / 2 , (gYPos + (gYSquares / 2.0)) * gSquareSize - gSize / 2, 0.02 * gSize + .02>);
128 }
129 }
130
131 touch_start(integer num) {
132 if(llDetectedKey(0) == gPlayer) {
133 llMessageLinked(LINK_ALL_CHILDREN, 5, (string)gXPos + "," + (string)gYPos, "");
134 } else {
135 // llWhisper(0, "It's not your turn, " + llKey2Name(llDetectedKey(0)));
136 }
137 }
138
139 link_message(integer sender, integer num, string str, key id) {
140 if(num == 5) {
141 list params = llParseString2List(str, [","], []);
142 gZoomPosX = llList2Integer(params, 0);
143 gZoomPosY = llList2Integer(params, 1);
144 state zoom2;
145 }
146 else if(num == 0) {
147 gPlayer = id;
148 } else if(num == 105 || num == 999) {
149 hide();
150 state ready;
151 }
152 }
153
154 changed(integer change) {
155 if(change & CHANGED_LINK)
156 if(llGetLinkNumber() == 0)
157 llDie(); // so i die
158 }
159 }
160
161 // move to zoomed in grid, each sensor covering one square on the board, wait for click.
162 state zoom2 {
163 state_entry() {
164 if(gXPosBase >= gXSensors || gYPosBase >= gYSensors || gZoomPosX + gYPosBase >= gGameSize || gZoomPosY + gXPosBase >= gGameSize) {
165 hide();
166 } else {
167 llSetScale(<gSquareSize, gSquareSize, 0.01 * gSize>);
168 llSetPos(<(gZoomPosX + gYPosBase + 0.5) * gSquareSize - gSize / 2, (gZoomPosY + gXPosBase + 0.5) * gSquareSize - gSize / 2, 0.02 * gSize + .02>);
169 }
170 }
171
172 touch_start(integer num) {
173 if(gZoomPosX + gYPosBase < gGameSize && gZoomPosY + gXPosBase < gGameSize) {
174 if(llDetectedKey(0) == gPlayer) {
176 integer x = gZoomPosX + gYPosBase;
177 integer y = gZoomPosY + gXPosBase;
178 llMessageLinked(LINK_ROOT, 4, (string)x + "," + (string)y, "");
179
180 state waiting;
181 } else {
182 // llWhisper(0, "It's not your turn, " + llKey2Name(llDetectedKey(0)));
183 }
184 }
185 }
186
187 link_message(integer sender, integer num, string str, key id) {
188 if(num == 0) {
189 gPlayer = id;
190 state zoom1;
191 } else if(num == 20) {
192 state waiting;
193 } else if(num == 105 || num == 999) {
194 hide();
195 state ready;
196 }
197 }
198
199 changed(integer change) {
200 if(change & CHANGED_LINK)
201 if(llGetLinkNumber() == 0)
202 llDie(); // so i die
203 }
204 }
205
206 // Move has been attempted, disable clicks and wait to see what to do next.
207 state waiting {
208 link_message(integer sender, integer num, string str, key id) {
209 if(num == 0) {
210 gPlayer = id;
211 state zoom1;
212 } else if(num == 21) {
213 state zoom2;
214 } else if(num == 105 || num == 999) {
215 hide();
216 state ready;
217 }
218 }
219
220 changed(integer change) {
221 if(change & CHANGED_LINK)
222 if(llGetLinkNumber() == 0)
223 llDie(); // so i die
224 }
225 }

Go_Game

And here's the GoSensorGridScript

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoSensorGridScript
2 // Jonathan Shaftoe
3 // Creates the sensor grid by rezzing 19 sensors (to make 20, including the one we're in) and
4 // giving each one a unique index. Script removes itself once done. Will take a few seconds
5 // to run, as rezzing has a built in delay. Requires link permission as all the created
6 // sensors are linked together.
7 // To use, rez a previously created GoSensor (see GoSensorScript), then put a GoSensor
8 // in that GoSensor's inventory, then drop this script in. Wait until it has done its stuff,
9 // then take the resulting GoSensorGrid into your inventory for later.
10
11 // If you only wanted to support smaller game boards and use less prims, you could change
12 // totalSensors below to the relevent number. For a 9x9 board, you would only need 9
13 // sensors, so make totalSensors 8 - for a 13x13 board, you need 16 sensors, so make
14 // totalSensors 15.
15
16 integer gNumSensors = 0; // how many have we made
17 integer gCountRez = 0; // how many are still waiting to be linked.
18
19 default
20 {
22 {
23 llSetObjectName("GoSensorGrid");
25 }
26
28 state start;
29 }
30 }
31
32 state start {
33 state_entry() {
34 llWhisper(0, "Creating sensors, please wait ...");
35 // 19x19 board needs 4x5 sensors, so 19 plus the one we're in to start with..
36 integer totalSensors = 19;
37 while(gNumSensors < totalSensors) {
38 llRezObject("GoSensor", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, gNumSensors + 1);
39 gNumSensors++;
40 gCountRez++;
41 }
42 }
43
44 object_rez(key id) {
45 llCreateLink(id, TRUE);
46 gCountRez--;
47 if(gCountRez == 0) {
49 } else if(gCountRez % 4 == 0) {
50 llWhisper(0, "... " + (string)gCountRez + " more sensors to make ...");
51 }
52 }
53
54 }

Go_Game

Now for the tiles to display textures, first GoTileScript

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoTileScript
2 // Jonathan Shaftoe
3 // A tile prim for displaying textures on the board to show where pieces are. Each tile is a squashed
4 // flattened cube, to create 3 square faces in a row, like XYText. Each face displays a texture which
5 // shows 4 game pieces. Using rotation and flipping, just 20 textures are required to display all
6 // 81 possible states of 4 squares on the board being either empty, black or white.
7 // The tile prims are made into a tile grid, in the same way as the sensors.
8 // Create a single prim, drop this script and the GoTileFaceScript in, then take the resulting
9 // GoTile object into your inventory and see GoTileGridScript
10
11 integer gGameSize; // size of game board, 9, 13 or 19
12 integer gIndex; // which tile in the grid are we
13 float gSize; // scaling factor
14
15 integer TILEWIDTH = 6; // absolute values for making us the right shape
16 integer TILEHEIGHT = 2;
17
18 integer gRotated = 0; // are we one of the rotated tiles used down the edge to conserve prim count
19
20 integer gXPosBase; // Our logical coordinates within the grid
21 integer gYPosBase;
22
23 integer gXPos; // Our actual physical coordinates
24 integer gYPos;
25
26 float gSquareSize; // Size of an individual square on the board.
27
28 integer gFace0Modified = 0; // has one of the three faces been modified and need re-texturing
29 integer gFace1Modified = 0;
30 integer gFace2Modified = 0;
31
32 list gStates = []; // The state of this tile. Each face is a string in the list, each
33 // string is 4 characters of 0 - blank, 1 - black, 2 - white
34 list gStatesBackup = []; // backup copy of game state used for undoing changes during endgame
35 integer gGotBackup = 0; // do we have a backup.
36 string TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c";
37
38 add_turn(integer face, integer x, integer y, string colour) {
39 string current = llList2String(gStates, face);
40 // llWhisper(0, "Got add turn, face: " + (string) face + " coords " + (string)x + ", " + (string)y + " and current state: " + current + ". Index is: " + (string)gIndex);
41 string newstr;
42 integer pos = (y * 2) + x;
43 if(pos == 0) {
44 newstr = colour + llGetSubString(current, 1, 3);
45 } else if(pos == 3) {
46 newstr = llGetSubString(current, 0, 2) + colour;
47 } else {
48 newstr = llGetSubString(current, 0, pos - 1) + colour + llGetSubString(current, pos + 1, 3);
49 }
50 // llWhisper(0, "New state is: " + newstr);
51 gStates = llListInsertList(llDeleteSubList(gStates, face, face), [newstr], face);
52 }
53
54 show_face(integer face) {
55 string statestr = llList2String(gStates, face);
56 llMessageLinked(llGetLinkNumber(), 301, statestr, (string)(face + 10 * gRotated));
57 }
58
59 default {
60 state_entry() {
62 PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0, ZERO_VECTOR, <0.333333, 1, 0>, ZERO_VECTOR,
63 PRIM_SIZE, <1.5, 0.5, 0.01>,
64 PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>, <0, 0, 0>, 0.0,
65 PRIM_COLOR, ALL_SIDES, <1, 1, 1>, 1.0
66 ]);
67 llSetObjectName("GoTile");
69 llSetObjectDesc("Go Game board tile");
71
72 }
73
74 on_rez(integer start_param) {
75 gIndex = start_param;
76 // llSetText((string)gIndex, <0, 0, 0>, 1.0);
77 state ready;
78 }
79
80 changed(integer change) {
81 if(change & CHANGED_LINK)
82 if(llGetLinkNumber() == 0)
83 llDie(); // so i die
84 }
85 }
86
87 state ready {
88 link_message(integer sender, integer num, string str, key id) {
89 if(num == 3) {
90 list params = llParseString2List(str, [","], []);
91 gGameSize = llList2Integer(params, 0);
92 gSize = llList2Float(params, 3);
93
94 integer vertical_strips = 0;
95 integer xTiles = gGameSize / TILEWIDTH;
96 integer yTiles = gGameSize / TILEHEIGHT;
97 if(yTiles * TILEHEIGHT < gGameSize) {
98 yTiles++;
99 }
100 if(xTiles * TILEWIDTH + 2 < gGameSize) {
101 xTiles++;
102 } else {
103 vertical_strips = xTiles + 1;
104 }
105 integer extra_index = gIndex - (xTiles * yTiles);
106 if(extra_index >= vertical_strips) {
107 // hide us away, not needed
108 llSetScale(<0.01, 0.01, 0.01>);
109 llSetPos(<0, 0, -.01>);
110 llSetText("", <0,0,0>, 1.0);
111 gXPos = -10;
112 gYPos = -10;
113 } else {
114 if(extra_index >= 0 && extra_index < vertical_strips) {
115 gXPosBase = xTiles;
116 gYPosBase = extra_index * (TILEWIDTH / TILEHEIGHT);
117 gRotated = 1;
118 } else {
119 gXPosBase = (gIndex % xTiles);
120 gYPosBase = (gIndex / xTiles);
121 }
122
123 gXPos = gXPosBase * TILEWIDTH;
124 gYPos = gYPosBase * TILEHEIGHT;
125
126 gSquareSize = gSize / gGameSize;
127 llSetTexture(TRANSPARENT, ALL_SIDES);
128 llSetScale(<gSquareSize * TILEWIDTH, gSquareSize * TILEHEIGHT, 0.01>);
129 if(gRotated == 1) {
130 llSetPos(<(gXPos + (TILEHEIGHT / 2.0)) * gSquareSize - gSize / 2, (gYPos + (TILEWIDTH / 2.0)) * gSquareSize - gSize / 2, 0.005 * gSize + .005>);
131 llSetLocalRot(llEuler2Rot(<0, 0, PI_BY_TWO>));
132 } else {
133 llSetPos(<(gXPos + (TILEWIDTH / 2.0)) * gSquareSize - gSize / 2, (gYPos + (TILEHEIGHT / 2.0)) * gSquareSize - gSize / 2, 0.01 * gSize + .001>);
134 }
135 gStates = ["0000", "0000", "0000"];
136 }
137 } else if(num == 201) {
138 list params = llParseString2List(str, [","], []);
139 integer x;
140 integer y;
141 string colour;
142 x = llList2Integer(params, 0);
143 y = llList2Integer(params, 1);
144 colour = llList2String(params, 2);
145 if(x < gXPos || y < gYPos) {
146 return;
147 }
148 integer face;
149 integer facex;
150 integer facey;
151 if(gRotated == 0) {
152 facey = y - gYPos;
153 if(facey > 1) {
154 return;
155 }
156 face = (x - gXPos) / 2;
157 facex = (x - gXPos) % 2;
158 } else {
159 facex = x - gXPos;
160 if(facex > 1) {
161 return;
162 }
163 face = (y - gYPos) / 2;
164 facey = (y - gYPos) % 2;
165 }
166 if(face > 2) {
167 return;
168 }
169 // need to save states in case we have to undo the changes - needed when marking groups for endgame
170 if(id == "1" && gGotBackup == 0) {
171 gGotBackup = 1;
172 gStatesBackup = gStates;
173 }
174 add_turn(face, facex, facey, colour);
175 if(colour != "0") {
176 show_face(face);
177 } else {
178 if(face == 0) {
179 gFace0Modified = 1;
180 } else if(face == 1) {
181 gFace1Modified = 1;
182 } else if(face == 2) {
183 gFace2Modified = 1;
184 }
185 }
186 } else if(num == 202) {
187 if(gFace0Modified == 1) {
188 show_face(0);
189 gFace0Modified = 0;
190 }
191 if(gFace1Modified == 1) {
192 show_face(1);
193 gFace1Modified = 0;
194 }
195 if(gFace2Modified == 1) {
196 show_face(2);
197 gFace2Modified = 0;
198 }
199 } else if(num == 15) {
200 if(gGotBackup == 1) {
201 gStates = gStatesBackup;
202 gStatesBackup = [];
203 gGotBackup = 0;
204 show_face(0);
205 show_face(1);
206 show_face(2);
207 }
208 } else if(num == 203) {
209 gGotBackup = 0;
210 gStatesBackup = [];
211 } else if(num == 999) {
212 gGotBackup = 0;
213 gStatesBackup = [];
214 gStates = ["0000", "0000", "0000"];
215 show_face(0);
216 show_face(1);
217 show_face(2);
218 gFace0Modified = 0;
219 gFace1Modified = 0;
220 gFace2Modified = 0;
221 }
222 }
223
224 changed(integer change) {
225 if(change & CHANGED_LINK)
226 if(llGetLinkNumber() == 0)
227 llDie(); // so i die
228 }
229 }

Go_Game

Due to script length restrictions, it wouldn't all fit in one, so there's another script required here, GoTileFaceScript

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoTileFaceScript
2 // Jonathan Shaftoe
3 // Texture-handling side of functionality required by GoTiles. Wouldn't all fit in one
4 // script due to script size limits. Also has problems with runtime size, hence the way
5 // the lists are joined together in bits. Contains a mapping from all of the possible
6 // game states of a 2x2 grid of squares, each of which being 0 - empty 1 - black or
7 // 2 - white, to the texture to be used to display it, and with what rotation/flipping.
8 // Also, each of the three faces on a tile has a different rotation to apply to make
9 // things all be the right way around.
10 // Some heavy borrowing from XYText in here - thank you!
11 // For usage, see GoTileScript
12
13
14 // List of 20 textures used to display board pieces.
15 list gTextureIndex = ["701917a8-d614-471f-13dd-5f4644e36e3c",
16 "b51f6506-2156-a8ea-15f5-172d80add211",
17 "d48ab9c0-c49d-e009-a893-84bd7326de84",
18 "3823e129-e09f-b0fb-8717-90bc88e8ef46",
19 "0f3113bb-39c2-e0b9-475e-00e59afd85b0",
20 "bc806a54-3b0e-8f96-c105-d9da31160be5",
21 "58b6e53a-8b13-726f-24e7-ec94798bc9c4",
22 "760aa9bf-c25f-6880-2292-0c3c56421985",
23 "633464b2-2d1b-b050-2af9-f6dd2427465f",
24 "9f71ccc5-63e1-f5f0-6407-43577019f408",
25 "ad62bcb5-a607-21da-66eb-15548aad6b0a",
26 "d56bad91-3d05-e90e-080f-31224b353c42",
27 "00e8a3c3-996e-ed81-5e37-a86b099a9be7",
28 "5b206598-0c5d-352f-14e2-d18b520919a6",
29 "cf839003-eaf4-4af3-abe9-5d8bf3f28f0a",
30 "ab6e5d0b-16b9-3aee-b898-8c532c5e691c",
31 "e05ef3e7-f2a3-4b8c-f4ea-86d8b00a1c20",
32 "b5014c6e-d0b6-c6c7-6c25-d532fe08476f",
33 "15c19f1d-ee3f-22ba-541c-c44c62ea75c7",
34 "3f563f39-1c4d-297a-3956-cc174a9209e6",
35 "6879b0ca-a295-dd20-1e08-cc60da377a1a"];
36
37 // Will contain complete list, put together from parts below. Striped list of 4 items,
38 // first is game state represented as a string, second is index into texture list for
39 // which texture to use, third is mirroring/flipping required of texture, fourth is
40 // rotation.
41 list gStateList = [];
42
43 // Index of prim faces for each of the three faces we're using to display textures
44 list gFaceIndex = [4, 0, 2];
45 // hack for apparent bug, can't just negate PI_BY_TWO.
46 float MINUS_PI_BY_TWO = -1.5707963267948966192313216916398;
47 // rotation needed for each of the three faces.
48 list gFaceRotations = [PI_BY_TWO, 0, MINUS_PI_BY_TWO];
49
50 // parts used to create gStateList, see boave.
51 list part1 = ["0000", 0, <1, 1, 0>, 0.0,
52 "1000", 1, <1, -1, 0>, 0.0,
53 "0100", 1, <-1, -1, 0>, 0.0,
54 "0010", 1, <1, 1, 0>, 0.0,
55 "0001", 1, <-1, 1, 0>, 0.0,
56 "2000", 2, <1, -1, 0>, 0.0,
57 "0200", 2, <-1, -1, 0>, 0.0,
58 "0020", 2, <1, 1, 0>, 0.0,
59 "0002", 2, <-1, 1, 0>, 0.0,
60 "1200", 3, <1, -1, 0>, 0.0,
61 "2100", 3, <-1, -1, 0>, 0.0,
62 "0012", 3, <1, 1, 0>, 0.0,
63 "0021", 3, <-1, 1, 0>, 0.0,
64 "1020", 3, <1, 1, 0>, PI_BY_TWO,
65 "2010", 3, <-1, 1, 0>, PI_BY_TWO];
66 list part2 = [
67 "0102", 3, <1, -1, 0>, PI_BY_TWO,
68 "0201", 3, <-1, -1, 0>, PI_BY_TWO,
69 "1002", 4, <1, -1, 0>, 0.0,
70 "0120", 4, <-1, -1, 0>, 0.0,
71 "2001", 4, <-1, 1, 0>, 0.0,
72 "0210", 4, <1, 1, 0>, 0.0,
73 "1100", 5, <1, -1, 0>, 0.0,
74 "0011", 5, <1, 1, 0>, 0.0,
75 "1010", 5, <1, 1, 0>, PI_BY_TWO,
76 "0101", 5, <1, -1, 0>, PI_BY_TWO,
77 "1001", 6, <-1, 1, 0>, 0.0,
78 "0110", 6, <1, 1, 0>, 0.0,
79 "2200", 7, <1, -1, 0>, 0.0,
80 "0022", 7, <1, 1, 0>, 0.0,
81 "2020", 7, <1, 1, 0>, PI_BY_TWO];
82 list part3 = [
83 "0202", 7, <1, -1, 0>, PI_BY_TWO,
84 "2002", 8, <-1, 1, 0>, 0.0,
85 "0220", 8, <1, 1, 0>, 0.0,
86 "1110", 9, <-1, -1, 0>, 0.0,
87 "1101", 9, <1, -1, 0>, 0.0,
88 "1011", 9, <-1, 1, 0>, 0.0,
89 "0111", 9, <1, 1, 0>, 0.0,
90 "0211", 10, <1, 1, 0>, 0.0,
91 "2011", 10, <-1, 1, 0>, 0.0,
92 "1102", 10, <1, -1, 0>, 0.0,
93 "1120", 10, <-1, -1, 0>, 0.0,
94 "1012", 10, <1, 1, 0>, PI_BY_TWO,
95 "1210", 10, <-1, 1, 0>, PI_BY_TWO,
96 "0121", 10, <1, -1, 0>, PI_BY_TWO,
97 "2101", 10, <-1, -1, 0>, PI_BY_TWO];
98 list part4 = [
99 "0112", 11, <1, 1, 0>, 0.0,
100 "1201", 11, <1, -1, 0>, 0.0,
101 "1021", 11, <-1, 1, 0>, 0.0,
102 "2110", 11, <-1, -1, 0>, 0.0,
103 "0122", 12, <1, 1, 0>, 0.0,
104 "2201", 12, <1, -1, 0>, 0.0,
105 "1022", 12, <-1, 1, 0>, 0.0,
106 "2210", 12, <-1, -1, 0>, 0.0,
107 "2021", 12, <1, 1, 0>, PI_BY_TWO,
108 "2120", 12, <-1, 1, 0>, PI_BY_TWO,
109 "0212", 12, <1, -1, 0>, PI_BY_TWO,
110 "1202", 12, <-1, -1, 0>, PI_BY_TWO,
111 "0221", 13, <1, 1, 0>, 0.0,
112 "2102", 13, <1, -1, 0>, 0.0,
113 "2012", 13, <-1, 1, 0>, 0.0];
114 list part5 = [
115 "1220", 13, <-1, -1, 0>, 0.0,
116 "2220", 14, <-1, -1, 0>, 0.0,
117 "2202", 14, <1, -1, 0>, 0.0,
118 "2022", 14, <-1, 1, 0>, 0.0,
119 "0222", 14, <1, 1, 0>, 0.0,
120 "1111", 15, <1, 1, 0>, 0.0,
121 "1121", 16, <1, 1, 0>, 0.0,
122 "2111", 16, <1, -1, 0>, 0.0,
123 "1112", 16, <-1, 1, 0>, 0.0,
124 "1211", 16, <-1, -1, 0>, 0.0,
125 "1122", 17, <1, 1, 0>, 0.0,
126 "2211", 17, <1, -1, 0>, 0.0,
127 "2121", 17, <1, 1, 0>, PI_BY_TWO,
128 "1212", 17, <1, -1, 0>, PI_BY_TWO];
129 list part6 = [
130 "1221", 18, <1, 1, 0>, 0.0,
131 "2112", 18, <-1, 1, 0>, 0.0,
132 "1222", 19, <1, 1, 0>, 0.0,
133 "2212", 19, <1, -1, 0>, 0.0,
134 "2122", 19, <-1, 1, 0>, 0.0,
135 "2221", 19, <-1, -1, 0>, 0.0,
136 "2222", 20, <1, 1, 0>, 0.0];
137 // 2111
138
139 default {
140 state_entry() {
141 gStateList = part1 + part2 + part3 + part4 + part5 + part6;
142 }
143
144 link_message(integer sender, integer num, string str, key id) {
145 if(num == 301) {
146 integer face = (integer)((string)id) % 10;
147 integer rotated = (integer)((string)id) / 10;
148 integer realface = llList2Integer(gFaceIndex, face);
149 string mystate;
150 if(rotated == 1) {
151 // need to horizontally flip the state we're looking for if we're part of a rotated tile.
152 mystate = llGetSubString(str, 1, 1) + llGetSubString(str, 3, 3) + llGetSubString(str, 0, 0) + llGetSubString(str, 2, 2);
153 } else {
154 mystate = str;
155 }
156 integer pos = llListFindList(gStateList, [mystate]);
157 if(pos == -1) {
158 llWhisper(0, "Failed to find state for: " + mystate);
159 return;
160 }
161 integer texture = llList2Integer(gStateList, pos + 1);
162 key realtexture = llList2Key(gTextureIndex, texture);
163 llSetPrimitiveParams([PRIM_TEXTURE, realface, realtexture, llList2Vector(gStateList, pos + 2), <0, 0, 0>, llList2Float(gStateList, pos + 3) + llList2Float(gFaceRotations, face)]);
164 }
165 }
166 }

Go_Game

As with the sensors, we need a utility script to create the grid, GoTileGridScript.

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoTileGridScript
2 // Jonathan Shaftoe
3 // Utility script to create a linked grid of GoTile prims, each with a unique index.
4 // To use, rez a previously created GoTile from your inventory (see GoTileScript), then
5 // put a GoTile into the inventory of the one you rezzed, then put this script into it.
6 // It will spend a while rezing and linking all the GoTiles required, then once finished
7 // the script will delete itself from the object, now called GoTileGrid, which you take
8 // into your inventory and save for later.
9
10 // By default, this creates 33 sensors to make a total of 34, needed to cover a 19x19 board.
11 // If you only want to support smaller board sizes, you can reduce the totalTiles variable
12 // below to reduce the prim usage. For example, a 9x9 board just uses 10 tiles, so you can
13 // set totalTiles to 9.
14
15
16 integer gNumTiles = 0; // how many have we created
17 integer gCountRez = 0; // how many are waiting to be rezzed and linked.
18
19 default
20 {
22 {
23 llSetObjectName("GoTileGrid");
25 }
26
28 state start;
29 }
30 }
31
32 state start {
33 state_entry() {
34 llWhisper(0, "Creating tiles, please wait ...");
35 // 19x19 board needs 34, 6x2 sensors, minus the one we're in to start with
36 integer totalTiles = 33;
37 // integer totalTiles = 9; // just 9x9 for testing
38 while(gNumTiles < totalTiles) {
39 llRezObject("GoTile", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, gNumTiles + 1);
40 gNumTiles++;
41 gCountRez++;
42 }
43 }
44
45 object_rez(key id) {
46 llCreateLink(id, TRUE);
47 gCountRez--;
48 if(gCountRez == 0) {
50 } else if(gCountRez % 4 == 0) {
51 llWhisper(0, "... " + (string)gCountRez + " more tiles to make ...");
52 }
53 }
54
55 }

Go_Game

You should now have a GoButton, a GoJoinButton, a GoSensorGrid and a GoTileGrid. Create a prim, drop an instance of each of those objects into its inventory, then finally put in the following two scripts which contain the main game logic, and you should have yourself a working Go board.

First off is the GoGameLogicScript which contains most of the actual game logic for handling captures and the like.

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoGameLogicScript
2 // Jonathan Shaftoe
3 // Contains most of the game logic code for the Go game. It's big and it's nasty. It uses recursion
4 // to do group capture detection, so can be slow.
5 // For usage, see GoGameMainScript
6
7 integer gGameSize = 9; // size of the game board, can be 9, 13 or 19
8
9 integer gBlackScore; // current scores (captured stones)
10 integer gWhiteScore;
11
12 integer gDeadWhite; // count of dead stones in endgame
13 integer gDeadBlack;
14
15 string gGameStatePrev; // Previous game state, used to check for Ko
16 string gGameState; // Game state - string representing the current state of the game board.
17 string gCurrentGame; // Another copy of the game state, used to store current state when working out
18 // captures.
19
20 list gGroup = []; // list of indexes of stones in (possibly captured) group.
21 integer gGroupLiberties; // How many liberties the currently being checked group has
22 integer gLibertyX; // coordinates of found liberty. We only care if there are no liberties, one liberty
23 integer gLibertyY; // or more than one (number if more than one doesn't matter), so we store the first
24 // found liberty here, then subsequently found liberties we check if they're the same
25 // as this one, and if not then we know we have more than one.
26 integer gSearchColour; // Are we looking for black or white stones
27
28 integer gTurn; // Whose turn is it, black or white
29 integer gLastWasPass; // So we know if we get two passes in a row
30 integer gTurnPreEndgame; // For turn handling during endgame
31
32 integer gGotAtari; // If we've found at least one group with only one liberty left after a stone
33 // is played, then we have an atari
34
35 list gToDelete; // list of indexes of stones to be removed due to capture (can be more than one group)
36
37 integer gGroupType; // type of group check being done, to avoid having to pass it through recursive calls.
38 // 0 - suicide check, 1 - normal turn check, 2 - endgame dead group marking, 3 - endgame scoring.
39 float gSize=4.0; // global scale factor.
40
41 set_player(integer turn, integer endgame, integer send_message) {
42 gTurn = turn;
43 if(send_message == 1) {
44 llMessageLinked(LINK_ROOT, 101, (string)gTurn, (key)((string)endgame));
45 }
46 }
47
48 integer get_board_state(integer x, integer y) {
49 integer index = x + 1 + ((y + 1) * (gGameSize + 2));
50 string num = llGetSubString(gGameState, index, index);
51 return (integer)num;
52 }
53
54 set_board_state(integer x, integer y, integer newstate) {
55 integer index = x + 1 + ((y + 1) * (gGameSize + 2));
56 string before = llGetSubString(gGameState, 0, index - 1);
57 string after = llGetSubString(gGameState, index + 1, llStringLength(gGameState) - 1);
58 gGameState = before + (string)newstate + after;
59 }
60
61 // Sets gameboard size and initialises gameState. Note 3s used around edge to make
62 // group detection easier (no boundary conditions, 3 is neither 1 (black) or 2 (white))
63 set_size(integer size) {
64 gGameSize = size;
65 if(gGameSize == 9) {
66 gGameState = "33333333333" +
67 "30000000003" +
68 "30000000003" +
69 "30000000003" +
70 "30000000003" +
71 "30000000003" +
72 "30000000003" +
73 "30000000003" +
74 "30000000003" +
75 "30000000003" +
76 "33333333333";
77 } else if(gGameSize == 13) {
78 gGameState = "333333333333333" +
79 "300000000000003" +
80 "300000000000003" +
81 "300000000000003" +
82 "300000000000003" +
83 "300000000000003" +
84 "300000000000003" +
85 "300000000000003" +
86 "300000000000003" +
87 "300000000000003" +
88 "300000000000003" +
89 "300000000000003" +
90 "300000000000003" +
91 "300000000000003" +
92 "333333333333333";
93 } else if(gGameSize == 19) {
94 gGameState = "333333333333333333333" +
95 "300000000000000000003" +
96 "300000000000000000003" +
97 "300000000000000000003" +
98 "300000000000000000003" +
99 "300000000000000000003" +
100 "300000000000000000003" +
101 "300000000000000000003" +
102 "300000000000000000003" +
103 "300000000000000000003" +
104 "300000000000000000003" +
105 "300000000000000000003" +
106 "300000000000000000003" +
107 "300000000000000000003" +
108 "300000000000000000003" +
109 "300000000000000000003" +
110 "300000000000000000003" +
111 "300000000000000000003" +
112 "300000000000000000003" +
113 "300000000000000000003" +
114 "333333333333333333333";
115 }
116 }
117
118 // functions below for doing recursive group detection. This is NOT efficient.
119 // on a 19x19 board, you can run out of script execution stack space if there are big
120 // groups. Could be improved.
121
122 recurse_check_util(integer x, integer y, integer dir) {
123 integer neighbour = get_board_state(x, y);
124 if(neighbour == gSearchColour) {
125 recurse_check(x, y, dir);
126 } else if(neighbour == 0 && gGroupType != 2) {
127 if(x != gLibertyX || y != gLibertyY) {
128 gLibertyX = x;
129 gLibertyY = y;
130 gGroupLiberties++;
131 }
132 } else if((neighbour == 2 || neighbour == 1) && gGroupType == 3) {
133 if(gGroupLiberties != neighbour) {
134 gGroupLiberties += neighbour;
135 }
136 }
137 }
138
139 recurse_check(integer x, integer y, integer dir) {
140 if(gGroupType == 1 && gGroupLiberties >= 2) {
141 return;
142 }
143 if(gGroupType == 0 && gGroupLiberties == 1) {
144 return;
145 }
146 if(llListFindList(gGroup, [x + (y * gGameSize)]) != -1) {
147 return;
148 }
149 gGroup = gGroup + [x + (y * gGameSize)];
150 if(dir != 0) {
151 recurse_check_util(x - 1, y, 1);
152 }
153 if(dir != 1) {
154 recurse_check_util(x + 1, y, 0);
155 }
156 if(dir != 2) {
157 recurse_check_util(x, y - 1, 3);
158 }
159 if(dir != 3) {
160 recurse_check_util(x, y + 1, 2);
161 }
162 }
163
164 integer check_captures(integer x, integer y, integer dir, integer type) {
165 integer groupSize = 0;
166 gGroup = [];
167 gGroupLiberties = 0;
168 gLibertyX = -1;
169 gLibertyY = -1;
170 gGroupType = type;
171 recurse_check(x, y, dir);
172
173 // if type == 0 then we're checking for a suicide play.
174 if(type == 3 || gGroupLiberties == 0) {
175 groupSize = llGetListLength(gGroup);
176 }
177 if(type == 1) { // finding captured groups in normal gameplay
178 if(gGroupLiberties == 1) {
179 gGotAtari = 1;
180 } else if(gGroupLiberties == 0) {
181 integer i = 0;
182 while(i < groupSize) {
183 integer index = llList2Integer(gGroup, i);
184 integer remx = index % gGameSize;
185 integer remy = index / gGameSize;
186 set_board_state(remx, remy, 0);
187 i++;
188 }
189 gToDelete = gToDelete + gGroup;
190 }
191 } else if(type == 2) { // marking dead groups in endgame
192 integer i = 0;
193 while(i < groupSize) {
194 integer index = llList2Integer(gGroup, i);
195 integer remx = index % gGameSize;
196 integer remy = index / gGameSize;
197 set_board_state(remx, remy, 0);
198 i++;
199 gToDelete = gToDelete + gGroup;
200 }
201 } else if(type == 3) { // counting territory space in endgame - gGroupLiberties used to store colour of neighbouring stones to work out territory owner. If both colours are found then they're added so result will be 3 (or more)
202 integer i = 0;
203 while(i < groupSize) {
204 integer index = llList2Integer(gGroup, i);
205 integer remx = index % gGameSize;
206 integer remy = index / gGameSize;
207 set_board_state(remx, remy, 3);
208 i++;
209 }
210 if(gGroupLiberties == 1) {
211 gBlackScore += groupSize;
212 } else if(gGroupLiberties == 2) {
213 gWhiteScore += groupSize;
214 }
215 }
216
217 return groupSize;
218 }
219
220 default {
221 state_entry() {
222 gBlackScore = 0;
223 gWhiteScore = 0;
224 gTurn = 0;
225 gLastWasPass = FALSE;
226 state playing;
227 }
228 }
229
230 // Normal state during game play.
231 state playing {
232 state_entry() {
233 }
234
235 link_message(integer sender, integer num, string str, key id) {
236 if(num == 100) {
237 set_size((integer)str);
238 gSize = (float)((string)id);
239 } else if(num == 4) {
240 list params = llParseString2List(str, [","], []);
241 integer x = llList2Integer(params, 0);
242 integer y = llList2Integer(params, 1);
243 if(get_board_state(x, y) != 0) {
244 llWhisper(0, "Cannot play there, already occupied.");
246 } else {
247 gCurrentGame = gGameState;
248 set_board_state(x, y, gTurn + 1);
249 gSearchColour = (1 - gTurn) + 1;
250 integer scorechange = 0;
251 gGotAtari = 0;
252 gToDelete = [];
253 if(get_board_state(x + 1, y) == gSearchColour) {
254 scorechange += check_captures(x + 1, y, 0, 1);
255 }
256 if(get_board_state(x - 1, y) == gSearchColour) {
257 scorechange += check_captures(x - 1, y, 1, 1);
258 }
259 if(get_board_state(x, y + 1) == gSearchColour) {
260 scorechange += check_captures(x, y + 1, 2, 1);
261 }
262 if(get_board_state(x, y - 1) == gSearchColour) {
263 scorechange += check_captures(x, y - 1, 3, 1);
264 }
265 if(scorechange > 0 && gGameState == gGameStatePrev) {
266 llWhisper(0, "Cannot play there due to ko");
267 gGameState = gCurrentGame;
269 return;
270 }
271 if(scorechange == 0) {
272 gSearchColour = gTurn + 1;
273 integer checkSuicide = check_captures(x, y, -1, 0);
274 if(checkSuicide > 0) {
275 llWhisper(0, "Cannot play there, suicide play.");
276 gGameState = gCurrentGame;
278 return;
279 }
280 }
282 if(gGotAtari == 1) {
283 if(gTurn == 0) {
284 llWhisper(0, "Black says 'atari'");
285 } else {
286 llWhisper(0, "White says 'atari'");
287 }
288 }
289 gGameStatePrev = gCurrentGame;
290 if(scorechange > 0) {
291 // let's actually do the removing now
292 integer i = 0;
293 integer delete_index;
294 integer remx;
295 integer remy;
296 while(i < scorechange) {
297 delete_index = llList2Integer(gToDelete, i);
298 remx = delete_index % gGameSize;
299 remy = delete_index / gGameSize;
300 llMessageLinked(LINK_ALL_CHILDREN, 201, (string)remx + "," + (string)remy + ",0", "");
301 i++;
302 }
304 string piece;
305 if(scorechange == 1) {
306 piece = "piece";
307 } else {
308 piece = "pieces";
309 }
310 if(gTurn == 0) {
311 gBlackScore += scorechange;
312 llWhisper(0, "Black captured " + (string)scorechange + " " + piece);
313 } else {
314 gWhiteScore += scorechange;
315 llWhisper(0, "White captured " + (string)scorechange + " " + piece);
316 }
317 llMessageLinked(LINK_ROOT, 400, (string)scorechange, (string)gTurn);
318 }
319 gLastWasPass = FALSE;
320 set_player(1 - gTurn, 0, 1);
321 llMessageLinked(LINK_ALL_CHILDREN, 201, (string)x + "," + (string)y + "," + (string)(2 - gTurn), "");
322 }
323 } else if(num == 10) {
324 if(gTurn == 0) {
325 llWhisper(0, "Black passes");
326 } else {
327 llWhisper(0, "White passes");
328 }
329 gTurn = 1 - gTurn;
330 gGameStatePrev = gGameState;
331 if(gLastWasPass == TRUE) {
332 llWhisper(0, "Consecutive passes, entering endgame.");
333 state endgame;
334 }
335 gLastWasPass = TRUE;
336 set_player(gTurn, 0, 1);
337 } else if(num == 999) {
338 state default;
339 }
340 }
341 }
342
343 // State during endgame, when players must mark any dead groups belonging to the other player.
344
345 state endgame {
346 state_entry() {
347 llMessageLinked(LINK_ROOT, 102, "", "");
348 gLastWasPass = FALSE;
349 gTurnPreEndgame = gTurn;
350 gCurrentGame = gGameState;
351 gDeadBlack = 0;
352 gDeadWhite = 0;
353 set_player(gTurn, 1, 1);
354 }
355
356 link_message(integer sender, integer num, string str, key id) {
357 if(num == 103) {
358 set_player((integer)str, (integer)((string)id), 0);
359 } else if(num == 4) {
360 list params = llParseString2List(str, [","], []);
361 integer x = llList2Integer(params, 0);
362 integer y = llList2Integer(params, 1);
363 if(get_board_state(x, y) != (1 - gTurn) + 1) {
364 llWhisper(0, "Invalid selection. Select your opponent's groups which are dead.");
366 } else {
367 gSearchColour = (1 - gTurn) + 1;
368 gToDelete = [];
369 integer scorechange = check_captures(x, y, -1, 2);
370 integer i = 0;
371 integer delete_index;
372 integer remx;
373 integer remy;
374 while(i < scorechange) {
375 delete_index = llList2Integer(gToDelete, i);
376 remx = delete_index % gGameSize;
377 remy = delete_index / gGameSize;
378 llMessageLinked(LINK_ALL_CHILDREN, 201, (string)remx + "," + (string)remy + ",0", "1");
379 i++;
380 }
382 if(gSearchColour == 1) {
383 gDeadBlack += scorechange;
384 } else {
385 gDeadWhite += scorechange;
386 }
387 set_player(gTurn, 1, 1);
388 }
389 } else if(num == 104) {
390 if(str == "It's fine") {
392 if(gTurn == gTurnPreEndgame) {
393 state scoring;
394 } else {
395 set_player(gTurn, 1, 1);
396 }
397 } else if(str == "Dispute") {
398 llWhisper(0, "Dead groups disputed, resuming play.");
399 gTurn = gTurnPreEndgame;
400 set_player(gTurn, 0, 1);
401 gGameState = gCurrentGame;
403 state playing;
404 }
405 } else if(num == 999) {
406 state default;
407 }
408 }
409 }
410
411 // Actually working out the final score.
412
413 state scoring {
414 state_entry() {
415 llMessageLinked(LINK_SET, 105, "", "");
416 llWhisper(0, "Calculating final scores. Please be patient ...\nWarning - large open areas on a large board can cause this to error due to lack of memory.");
417 llSetText("Calculating final scores ...", <0, 0, 1>, 1.0);
418 integer x;
419 integer y;
420 gSearchColour = 0;
421 for (x = 0; x < gGameSize; x++) {
422 for (y = 0; y < gGameSize; y++) {
423 if(get_board_state(x, y) == 0) {
424 check_captures(x, y, -1, 3);
425 }
426 }
427 }
428 gBlackScore += gDeadWhite;
429 gWhiteScore += gDeadBlack;
430 llWhisper(0, "Final score, black: " + (string)gBlackScore + " and white: " + (string)gWhiteScore);
431 llSetText("Final score, black: " + (string)gBlackScore + " and white: " + (string)gWhiteScore, <0, 0, 1>, 1.0);
432 llMessageLinked(LINK_ROOT, 106, "", "");
433 }
434
435 link_message(integer sender, integer num, string str, key id) {
436 if(num == 999) {
437 state default;
438 }
439 }
440 }

Go_Game

And now for the GoGameMainScript, which does all the gluing together of all the other scripts, and handles players joining/leaving the game and the like.

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // GoGameMainScript
2 // Jonathan Shaftoe
3 // This is the main controlling script which sorts out rezzing all the objects and moving
4 // them around and sending messages to the other bits and pieces. It's big and it's messy.
5 // To use, create a single prim, drop into it a previously created GoButton, GoJoinButton,
6 // GoSensorGrid and GoTileGrid, then drop in the GoGaneLogicScript, then finally drop in this
7 // script. If you want the info button to give out a notecard, create one called Go Info and
8 // drop that into the inventory of the prim too (actually, it'll probably error if you don't
9 // do this, so.. )
10
11 // Channel used for listens, used for dialogs.
12 integer CHANNEL = 85483;
13 // Size of go board, 9, 13 or 19. This is just what it initialises to, can be changed
14 // during game setup
15 integer gGameSize = 9;
16
17 // Store the current listener, so we can remove it.
18 integer gListener;
19 // This is a general scaling multiplier, allowing you either to create a small dinky Go Board, or a
20 // large one. The maximum size that will work is 9.0, as beyond that linking of things fails. I've
21 // not experimented with the smallest workable size.
22 float SIZE = 3.0;
23
24 // Keys and names of the players playing each colour
25 key gBlackPlayer;
26 key gWhitePlayer;
27 string gBlackName;
28 string gWhiteName;
29 // Current player, whose turn it is.
30 key gPlayer;
31
32 // How many sensors we have, and the size of grid being used.
33 integer gXSensors;
34 integer gYSensors;
35
36 // Current scores
37 integer gBlackScore = 0;
38 integer gWhiteScore = 0;
39
40 // Used for counting rezzes, so we know when everything has been rezzedd
41 integer gCountRez;
42
43 // Whose turn it is, 0 is black, 1 is white.
44 integer gTurn = 0;
45
46 // How long since last turn (for reset)
47 string gLastTurnDate;
48 float gLastTurnTime;
49
50 // for the alpha part of coords, so we know what to call each square in messages. Note
51 // absence of I to avoid confusion with 1.
52 string COORDS = "ABCDEFGHJKLMNOPQRST";
53
54
55 set_player(integer turn, integer endgame, integer send_message) {
56 vector colour;
57 integer score = 0;
58 string name;
59 gTurn = turn;
60 if(gTurn == 0) {
61 gPlayer = gBlackPlayer;
62 score = gBlackScore;
63 colour = <0, 0, 0>;
64 name = gBlackName;
65 } else {
66 gPlayer = gWhitePlayer;
67 score = gWhiteScore;
68 colour = <1, 1, 1>;
69 name = gWhiteName;
70 }
71 llMessageLinked(LINK_ALL_CHILDREN, 0, "", gPlayer);
72 if(endgame == 0) {
73 llSetText(name + "'s turn (prisoners: " + (string)score + ").", colour, 1.0);
74 } else if(endgame == 1) {
75 llSetText(name + "'s turn to remove dead groups (prisoners: " + (string)score + ").", colour, 1.0);
76 llInstantMessage(gPlayer, "Select opponent's dead groups then click done.");
77 }
78 if(send_message == 1) {
79 llMessageLinked(LINK_ROOT, 103, (string)gTurn, (string)endgame);
80 }
81 gLastTurnDate = llGetDate();
82 gLastTurnTime = llGetGMTclock();
83 }
84
85 set_size(integer size) {
86 gGameSize = size;
87 if(gGameSize == 9) {
88 gXSensors = 3;
89 gYSensors = 3;
90 llSetTexture("a9878863-732f-09af-b908-09070ea9b213", 0);
91 } else if(gGameSize == 13) {
92 gXSensors = 4;
93 gYSensors = 4;
94 llSetTexture("a0973434-028b-3323-b572-8347095e6c3c", 0);
95 } else if(gGameSize == 19) {
96 gXSensors = 4;
97 gYSensors = 5;
98 llSetTexture("9575b5cf-72ec-14bf-e400-320f9008bbd9", 0);
99 }
100 llSetScale(<(gGameSize + 1.0) * SIZE / gGameSize, (gGameSize + 1.0) * SIZE / gGameSize, 0.01 * SIZE>);
101 llMessageLinked(LINK_ALL_CHILDREN, 3, (string)gGameSize + "," + (string)gXSensors + "," + (string)gYSensors + "," + (string)SIZE, "");
102 llMessageLinked(LINK_ROOT, 100, (string)gGameSize, (string)SIZE);
103 }
104
105 // State we start in, but don't stay in long. Need permission to link to do set-up.
106 default {
107 state_entry() {
109 PRIM_TYPE, PRIM_TYPE_BOX, PRIM_HOLE_DEFAULT, <0, 1, 0>, 0.0,
110 ZERO_VECTOR, <1, 1, 0>, ZERO_VECTOR,
111 PRIM_SIZE, <1 * SIZE, 1 * SIZE, 0.01 * SIZE>,
112 PRIM_TEXTURE, ALL_SIDES, "5748decc-f629-461c-9a36-a35a221fe21f", <1, 1, 0>, <0, 0, 0>, 0.0,
113 PRIM_COLOR, ALL_SIDES, <0.8, 0.6, 0.5>, 1.0
114 ]);
115 llSetObjectName("Jonathan's Go Board");
116 llSetObjectDesc("Jonathan's Go Board");
119 }
120
123 state initialising;
124 }
125 }
126
127 // Initialisation state, rezzing all the buttons we need
128 state initialising {
129 state_entry() {
130 gCountRez = 7;
131 llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * .45, SIZE * .02>, ZERO_VECTOR, llGetRot(), 1);
132 llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * .25, SIZE * .02>, ZERO_VECTOR, llGetRot(), 2);
133 llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * -.25, SIZE * .02>, ZERO_VECTOR, llGetRot(), 4);
134 llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), SIZE * -.45, SIZE * .02>, ZERO_VECTOR, llGetRot(), 3);
135 llRezObject("GoButton", llGetPos() + <SIZE * (0.5 + 0.2), 0, SIZE * .02>, ZERO_VECTOR, llGetRot(), 5);
136 llRezObject("GoJoinButton", llGetPos() + <0, SIZE * 0.5, 0.1 * SIZE>, ZERO_VECTOR, llGetRot(), 1);
137 llRezObject("GoJoinButton", llGetPos() + <0, SIZE * -0.5, 0.1 * SIZE>, ZERO_VECTOR, llGetRot(), 2);
138 }
139
140 object_rez(key id) {
141 llCreateLink(id, TRUE);
142 gCountRez--;
143 if(gCountRez == 0) {
145 state setup_sensors;
146 }
147 }
148 }
149
150 // Initialization state 2, seting up the sensor and tile grids.
151 state setup_sensors {
152 state_entry() {
153 gCountRez = 2;
154
155 llRezObject("GoSensorGrid", llGetPos() + <0, 0, 0.005 * SIZE + .01>, ZERO_VECTOR, llGetRot(), 0);
156 llRezObject("GoTileGrid", llGetPos() + <0, 0, 0.005 * SIZE + 0.1>, ZERO_VECTOR, llGetRot(), 0);
157 }
158
159 object_rez(key id) {
160 llCreateLink(id, TRUE);
161 gCountRez--;
162 if(gCountRez == 0) {
163 set_size(9);
164 state awaiting_start;
165 }
166 }
167 }
168
169 // All initialised, waiting for players to join the game, plus can change game board size.
170
171 state awaiting_start {
172 state_entry() {
173 llSetTouchText("Game Size");
174 llSetText("Go Game - awaiting players", <0, 0, 0>, 1.0);
175 }
176
177 state_exit() {
179 }
180
181 touch_start(integer num) {
182 llDialog(llDetectedKey(0), "Set size of Go game board.\nCurrently set to: " + (string)gGameSize + "x" + (string)gGameSize, ["9x9", "13x13", "19x19"], CHANNEL);
183 llListenRemove(gListener);
184 gListener = llListen(CHANNEL, "", llDetectedKey(0), "");
185 // Make sure we timeout the listen, in case they ignore the dialog
186 llSetTimerEvent(60 * 2);
187 }
188
189 listen(integer channel, string name, key id, string message) {
190 if(message == "9x9") {
191 set_size(9);
192 } else if(message == "13x13") {
193 set_size(13);
194 } else if(message == "19x19") {
195 set_size(19);
196 }
197 llWhisper(0, "Go board set to size: " + (string)gGameSize + "x" + (string)gGameSize);
198 llListenRemove(gListener);
200 }
201
202 timer() {
203 llListenRemove(gListener);
205 }
206
207 link_message(integer sender, integer num, string str, key id) {
208 if(num == 1) {
209 gBlackPlayer = id;
210 gBlackName = llKey2Name(id);
211 llWhisper(0, gBlackName + " will play black.");
212 } else if(num == 2) {
213 gWhitePlayer = id;
214 gWhiteName = llKey2Name(id);
215 llWhisper(0, gWhiteName + " will play white.");
216 } else if(num == 12) {
217 llWhisper(0, llKey2Name(id) + " resets the Go board.");
218 state resetting;
219 } else if(num == 14) {
220 llGiveInventory(id, "Go Info");
221 }
222 if(num == 1 || num == 2) {
223 if(gBlackPlayer != "" && gWhitePlayer != "") {
224 llWhisper(0, "Game started.");
225 set_player(0, 0, 1);
226 state playing;
227 }
228 }
229 }
230 }
231
232 // Game has started, two players are playing.
233 state playing {
234 state_entry() {
235 llSetTouchText("Undo zoom");
236 }
237
238 touch_end(integer num) {
239 if(llDetectedKey(0) == gPlayer) {
240 llMessageLinked(LINK_ALL_CHILDREN, 0, "", gPlayer);
241 }
242 }
243
244 link_message(integer sender, integer num, string str, key id) {
245 if(num == 101) {
246 set_player((integer)str, (integer)((string)id), 0);
247 } else if(num == 400) {
248 integer turn = (integer)((string)id);
249 integer score = (integer)str;
250 if(turn == 0) {
251 gBlackScore += score;
252 } else {
253 gWhiteScore += score;
254 }
255 } else if(num == 500) {
256 integer x = (integer)str;
257 integer y = (integer)((string)id);
258 llPlaySound("a46a5924-5679-6483-233a-5b0b165ec477", 1.0);
259 string player;
260 if(gTurn == 0) {
261 player = "Black";
262 } else {
263 player = "White";
264 }
265 string coord = llGetSubString(COORDS, x, x);
266 llWhisper(0, player + " played at " + coord + ", " + (string)(y + 1));
267 } else if(num == 102) {
268 state endgame;
269 } else if(num == 12) {
270 string nowDate = llGetDate();
271 float nowTime = llGetGMTclock();
272 if(nowDate == gLastTurnDate && ((nowTime - gLastTurnTime) < 600)) {
273 llInstantMessage(id, "You cannot reset the Go board, an active game is in progress. If you are playing and wanting to reset, resign first to end a game. If a game has been abandoned, then the board will be resettable if 10 minutes pass with no turn played.");
274 } else {
275 llDialog(id, "Are you sure you want to reset the board? The game in progress will be lost.", ["Reset", "Cancel"], CHANNEL);
276 llListenRemove(gListener);
277 gListener = llListen(CHANNEL, "", id, "");
278 llSetTimerEvent(60 * 4);
279 }
280 } else if(num == 13) {
281 if(id == gWhitePlayer || id == gBlackPlayer) {
282 llDialog(id, "Are you sure you want to resign the game?", ["Resign", "Cancel"], CHANNEL);
283 llListenRemove(gListener);
284 gListener = llListen(CHANNEL, "", id, "");
285 llSetTimerEvent(60 * 4);
286 }
287 } else if(num == 14) {
288 llWhisper(0, "Game in progress between " + gBlackName + " (Black: current prisoners " + (string)gBlackScore + ") and " + gWhiteName + " (White, current prisoners: " + (string)gWhiteScore + ")");
289 llGiveInventory(id, "Go Info");
290 }
291 }
292
293 listen(integer channel, string name, key id, string message) {
294 llListenRemove(gListener);
296 if(message == "Resign") {
297 string resignerName;
298 string winnerName;
299 if(id == gBlackPlayer) {
300 resignerName = "Black";
301 winnerName = "White";
302 } else {
303 resignerName = "White";
304 winnerName = "Black";
305 }
306 llWhisper(0, resignerName + " has resigned. " + winnerName + " wins.");
307 llSetText(resignerName + " resigns. " + winnerName + " wins.", <0, 0, 1>, 1.0);
308 state gameover;
309 } else if(message == "Reset") {
310 llWhisper(0, name + " resets the Go board.");
311 state resetting;
312 }
313 }
314
315 timer() {
316 llListenRemove(gListener);
318 }
319
320 state_exit() {
322 }
323 }
324
325 // Two passes have occured, we're in endgame, players must mark oppositions dead groups (and then
326 // agree with what their opponent has marked as dead )
327
328 state endgame {
329 state_entry() {
330 }
331
332 link_message(integer sender, integer num, string str, key id) {
333 if(num == 11) {
334 set_player(1 - gTurn, 2, 1);
335 llDialog(gPlayer, "Are you happy with the groups your opponent has marked as dead?\nIf you dispute, play will resume.", ["It's fine", "Dispute"], CHANNEL);
336 llListenRemove(gListener);
337 gListener = llListen(CHANNEL, "", gPlayer, "");
338 // Make sure we timeout the listen, in case they ignore the dialog
339 llSetTimerEvent(60 * 4);
340 } else if(num == 105) {
341 state scoring;
342 } else if(num == 101) {
343 set_player((integer)str, (integer)((string)id), 0);
344 } else if(num == 12) {
345 string nowDate = llGetDate();
346 float nowTime = llGetGMTclock();
347 if(nowDate == gLastTurnDate && ((nowTime - gLastTurnTime) < 600)) {
348 llInstantMessage(id, "You cannot reset the Go board, an active game is in progress. If you are playing and wanting to reset, resign first to end a game. If a game has been abandoned, then the board will be resettable if 10 minutes pass with no turn played.");
349 } else {
350 llDialog(id, "Are you sure you want to reset the board? The game in progress will be lost.", ["Reset", "Cancel"], CHANNEL);
351 llListenRemove(gListener);
352 gListener = llListen(CHANNEL, "", id, "");
353 llSetTimerEvent(60 * 4);
354 }
355 } else if(num == 14) {
356 llWhisper(0, "Game in progress between " + gBlackName + " (Black: current prisoners " + (string)gBlackScore + ") and " + gWhiteName + " (White, current prisoners: " + (string)gWhiteScore + ") - players in endgame");
357 llGiveInventory(id, "Go Info");
358 }
359 }
360
361 listen(integer channel, string name, key id, string message) {
362 llListenRemove(gListener);
364 llMessageLinked(LINK_ROOT, 104, message, "");
365 if(message == "Dispute") {
366 state playing;
367 } else if(message == "Reset") {
368 llWhisper(0, name + " resets the Go board.");
369 state resetting;
370 }
371 }
372
373 touch_end(integer num) {
374 if(llDetectedKey(0) == gPlayer) {
375 llMessageLinked(LINK_ALL_CHILDREN, 0, "", gPlayer);
376 }
377 }
378
379 timer() {
380 llListenRemove(gListener);
382 }
383
384 state_exit() {
386 }
387 }
388
389 // End game finished, waiting for GoGameLogic to work out final score.
390 state scoring {
391 state_entry() {
392 }
393
394 link_message(integer sender, integer num, string str, key id) {
395 if(num == 106) {
396 state gameover;
397 } else if(num == 14) {
398 llWhisper(0, "Game in progress between " + gBlackName + " (Black: current prisoners " + (string)gBlackScore + ") and " + gWhiteName + " (White, current prisoners: " + (string)gWhiteScore + ") - game scoring.");
399 llGiveInventory(id, "Go Info");
400 }
401 }
402 }
403
404 // Game finished and final score displayed
405 state gameover {
406 state_entry() {
407 }
408 link_message(integer sender, integer num, string str, key id) {
409 if(num == 12) {
410 state resetting;
411 } else if(num == 14) {
412 llGiveInventory(id, "Go Info");
413 }
414 }
415 }
416
417 // Reset board ready to start new game.
418 state resetting {
419 state_entry() {
420 llMessageLinked(LINK_SET, 999, "", "");
421 set_size(gGameSize);
422 gBlackPlayer = "";
423 gWhitePlayer = "";
424 gBlackScore = 0;
425 gWhiteScore = 0;
426 state awaiting_start;
427 }
428 }

Go_Game

Here is the text of the Go Info notecard given out by the info button. This should be saved as a notecard called Go Info and dropped in the inventory of the go game prim.

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 Hello and welcome to Jonathan's Go game.
2
3 Go is an ancient game from Asia with simple rules but complex gameplay. Two players, white and black, take turns to place a stone on a board. If a group of one colour is completely surrounded by a group of another, it is removed from the board. Note that stones are placed on the junctions of lines, not in the spaces between them, and that diagonals do not count. Once both players agree that there is no further advantage in playing any further stones, the one with the greatest score wins.
4
5 A full description of the rules of Go is beyond the scope of a notecard. There are many websites with descriptions and tutorials, for example:
6
7 http://www.britgo.org/
8
9 There are two types of scoring in common use, Japanese or Territory scoring, and Chinese or Area scoring. I've implemented Territory scoring. In practice there is little difference between the two.
10
11 Though the basic rules are implemented, there are some unusual circumstances which aren't covered, for example triple ko or seki. In the unlikely event of these occuring, you'll have to sort out how to handle it between yourselves.
12
13 I have designed the board to be hopefully as intuitive to use as possible. Before starting a game, the size of the game board can be set by clicking anywhere on the board and selecting from the three commonly used sizes on offer. A 9x9 game can be played relatively quickly, but loses the deeper levels of strategy involved in a larger game. It is a good choice for beginners. A 19x19 game is a proper full sized game, and can take upwards of two hours to complete. 13x13 is a midpoint between the two.
14
15 Join a game by clicking one of the two join buttons. Once two players have joined, the game will start, and only those two players will be able to use the board until the game is finished, or the board is reset. The board can only be reset if no move has been played for a certain amount of time (currently set to 10 minutes) - this prevents boards being left in a used state.
16
17 Place a stone on the board by clicking first on the coloured translucent square over the area you want to play in, then on the actual square over the point you want to play. This two step process is to conserve the prim usage of the board. If after the first click you change your mind, you can 'unzoom' by clicking anywhere else on the board. If you try to play somewhere illegal, due to ko or suicide for example, the board will tell you.
18
19 Once both players have passed consecutively, the board enters end-game. During this stage, each player in turn is invited to select which of his or her opponent's stones are 'dead' - that is stones whose capture is inevitable and unpreventable, generally all groups lacking two distinct 'eyes' (holes inside the group). Selecting a stone will remove all of the stones in that connected group. Once each player has signified that they have completed doing this, by pressing 'Done', the other is asked if they agree with the stones removed. If not, they can dispute the endgame and play resumes. It is essential to remove dead groups, as the automatic scoring cannot otherwise work.
20
21 Once both players have selected their opponent's dead groups and had their selection confirmed, final scoring is calculated. This can take a while, depending on the size of board and size of territory to be counted. Unfortunately due to the limits of LSL in unusual circumtances, for example on a large board with a large area of territory, this can cause an out of memory error in the script. Please do not enter endgame scoring on a sparsely populated board, use the 'resign' button instead.
22
23 Enjoy!
24
25 Jonathan Shaftoe

Go_Game

And finally, here's some documentation I did for myself on the many link messages used. If you're going to try to figure out how it all works or change the code, this will probably be invaluable!

Category: Games
By : Jonathan Shaftoe
Created: 2010-01-10 Edited: 2010-01-10
Worlds: Second Life

1 // Messages
2 // 0. From MainScript to Button, Sensor - sets current player, black or white, and resets Sensor to zoom1
3 // 1. From MainScript, to JoinButton, Button. Sets SIZE
4 // 2. From MainScript, to ?. Sets SIZE and gGameSize (depricated?)
5 // 1 & 2 From JoinButton to MainScript - sets who's playing black/white
6 // 3. From MainScript, to Tile, Sensor, sets gGameSize, gX/YSensors and SIZE, sent with set_size
7 // 4. From Sensor to Logic, turn attempt made, coordinates passed.
8 // 5. From Sensor to Sensor. coordinates of zoom from zoom1 to zoom2.
9 // 10. From Button, to Logic, player pressed 'pass'
10 // 11. From Button to Main, player pressed 'done'
11 // 12. From Button to Main, player pressed 'reset'
12 // 13. From Button to Main, player pressed 'resign'
13 // 14. From Button to Main, player pressed 'info'
14 // 15. From Logic, to Tile, endgame disputed, reset tile to previous state
15 // 20. From Sensor to Sensor, go to waiting state, move attempted
16 // 21. From Logic to Sensor, go back from waiting state to zoom2, attempt to move failed.
17 // 100. From Main to Logic, sets gGameSize and SIZE, sent with every set_size
18 // 101. From Logic, to Main, sets whose turn and whether we're in endgame.
19 // 102. From Logic to Main, entering endgame after two passes
20 // 103. From Main to Logic, sets current player/endgame state
21 // 104. From Main to Logic, whether player disputes endgame/dead groups or not
22 // 105. From Logic to Main and Sensors, doing scoring (so sensors hide)
23 // 106. From Logic to Main, from scoring to gameover
24 // 201. From Logic to Tile, display piece, x, y, colour (1, 2) passed (displays immediately) or 0 to remove (doesn't display until ..).
25 // 202. From Logic to Tile, display result of removes from 201 messages.
26 // 203. From Logic to Tile, clears backup states (for endgame disputes) as agreed.
27 // 301. From Tile to TileFace, do actual display for given state.
28 // 400. From Logic to Main, send new captures done by given player for score update
29 // 500. From Logic to Main, turn played, so do message and do sound
30 // 999. From Main, to JoinButton, Sensors, Tile, Logic - game reset.
31
32 // Main sends: 0, 1, 2, 3, 100, 103, 104, 999
33 // Main receives: 1, 2, 11, 12, 13, 14, 101, 102, 105, 106, 400, 500
34
35 // TileFace sends: none
36 // TileFace receives: 301
37
38 // Tile sends: 301
39 // Tile receives: 3, 15, 201, 202, 203, 999
40
41 // Logic sends: 15, 21, 101, 102, 105, 106, 201, 202, 203, 400, 500
42 // Logic receives: 4, 10, 100, 103, 104, 999
43
44 // Button sends: 10, 11, 12, 13, 14
45 // Button receives: 0, 1
46
47 // Sensor sends: 4, 5, 20
48 // Sensor receives: 0, 3, 5, 20, 21, 105, 999
49
50 // JoinButton sends: 1, 2
51 // JoinButton receives: 1, 999

Back to the Best Free Tools in Second Life and OpenSim.