cleaned all trailing white space from source files.
[sdk] / samples / games / othello / othello.ec
1 /****************************************************************************
2    OTHELLO Game
3
4    Copyright (c) 2001-2004 Jerome Jacovella-St-Louis
5    All Rights Reserved.
6 ****************************************************************************/
7 import "ecere"
8
9 // --- Definitions ---
10 enum ChipColor : byte { blue, green, empty };
11
12 define SERVER = blue;
13 define CLIENT = green;
14
15 define NUM_COLUMNS = 8;
16 define NUM_ROWS = 8;
17
18 define WIDTH = 40;
19 define HEIGHT = 40;
20
21 define OTHELLO_PORT = 7777;
22
23 enum OthelloMessage : byte { newGame = 1, position };
24
25 struct OPacket
26 {
27    OthelloMessage type;
28    ChipColor player;
29    byte x,y;
30 };
31
32 Othello othello {};
33
34 // --- Othello Window Class ---
35 class Othello : Window
36 {
37    // Data Structure
38    bool hosting, gameRunning, local;
39    ChipColor turn;
40    int numChips[ChipColor];
41    OthelloSocket sockets[ChipColor];
42    ChipColor board[NUM_ROWS][NUM_COLUMNS];
43    bool gameOver;
44    OthelloService service { port = OTHELLO_PORT, othello = this };
45
46    // Default properties
47    background = black, tabCycle = true, text = "ECERE Othello", borderStyle = sizable, hasClose = true,
48    size.h = 480, clientSize.w = NUM_COLUMNS*WIDTH;
49
50    // --- Othello Utilities ---
51    int TurnChipsDirection(int sx, int sy, int dx, int dy, ChipColor color, int turn)
52    {
53       int x,y;
54       int numChips = 0;
55
56       sx += dx;
57       sy += dy;
58
59       // Out of the board...
60       if(sx < 0 || sx >= NUM_COLUMNS || sy < 0 || sy >= NUM_ROWS)
61          return 0;
62
63       // Chip of the same color...
64       if(board[sy][sx] == color || board[sy][sx] == empty)
65          return 0;
66
67       for(x = sx, y = sy;
68           x >= 0 && x < NUM_COLUMNS && y >= 0 && y < NUM_ROWS;
69           x += dx, y += dy)
70       {
71          // Chip of the same color at the end of the line...
72          if(board[y][x] == color)
73          {
74             if(turn)
75             {
76                for(x = sx, y = sy;
77                    x >= 0 && x < NUM_COLUMNS && y >= 0 && y < NUM_ROWS;
78                    x += dx, y += dy)
79                {
80                   if(board[y][x] == color)
81                      break;
82                   else
83                      board[y][x] = color;
84                }
85             }
86             return numChips;
87          }
88          // Empty case...
89          else if(board[y][x] == empty)
90             return 0;
91          // Chip of the opponent's color...
92          else
93             numChips ++;
94       }
95
96       // No chip of the same color at the end...
97       return 0;
98    }
99
100    bool PossibleMove()
101    {
102       bool validMove = false;
103       int x,y;
104       for(y = 0; y<NUM_ROWS && !validMove; y++)
105          for(x = 0; x<NUM_COLUMNS && !validMove; x++)
106             if(board[y][x] == empty && TurnChips(x, y, turn, 0))
107                validMove = true;
108       return validMove;
109    }
110
111    int TurnChips(int sx, int sy, ChipColor color, int turn)
112    {
113       int x,y;
114       int numChips = 0;
115
116       for(y = -1; y <= 1; y++)
117          for(x = -1; x <= 1; x++)
118             if(x || y)
119                numChips += TurnChipsDirection(sx, sy, x,y, color, turn);
120       if(numChips && turn)
121       {
122          board[sy][sx] = color;
123          this.numChips[color] += numChips + 1;
124          this.numChips[1-color] -= numChips;
125          this.turn = 1-color;
126
127          if(!PossibleMove())
128          {
129             this.turn = color;
130             if(!PossibleMove())
131                gameOver = true;
132          }
133
134          Update(null);
135       }
136       return numChips;
137    }
138
139    void NewGame()
140    {
141       int x,y;
142
143       for(y = 0; y<NUM_ROWS; y++)
144          for(x = 0; x<NUM_COLUMNS; x++)
145             board[y][x] = empty;
146       board[3][3] = board[4][4] = blue;
147       board[3][4] = board[4][3] = green;
148       numChips[blue] = numChips[green] = 2;
149       turn = CLIENT;
150       gameOver = false;
151       Update(null);
152    }
153
154    void EnableButtons()
155    {
156       join.disabled = false;
157       host.disabled = false;
158       disconnect.disabled = false;
159       stop.disabled = false;
160       localGame.disabled = false;
161
162       if(gameRunning || hosting || sockets[SERVER])
163          localGame.disabled = true;
164       if(!hosting)
165          stop.disabled = true;
166       if(!gameRunning && !sockets[SERVER])
167          disconnect.disabled = true;
168
169       if(hosting || sockets[SERVER] || local)
170          host.disabled = true;
171
172       if(sockets[SERVER] || local || hosting)
173          join.disabled = true;
174
175       address.disabled = join.disabled;
176    }
177
178    // Children
179    Button localGame
180    {
181       parent = this, text = "Local Game", position = Point{ 160, 330 }, size = Size { 100, 20 }, hotKey = altL;
182
183       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
184       {
185          local = true;
186          gameRunning = true;
187          EnableButtons();
188          NewGame();
189          Update(null);
190          return true;
191       }
192    };
193
194    Button host
195    {
196       parent = this, text = "Host", position = Point { 10, 330 }, size = Size { 60, 20 }, hotKey = altH;
197       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
198       {
199          if(service.Start())
200          {
201             hosting = true;
202             EnableButtons();
203          }
204          Update(null);
205          return true;
206       }
207    };
208
209    Button join
210    {
211       parent = this, text = "Join", position = Point{ 10, 370 }, size = Size { 40, 20 }, hotKey = altJ;
212       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
213       {
214          OthelloSocket socket { othello = this };
215          if(socket.Connect(address.line.text, OTHELLO_PORT))
216          {
217             sockets[SERVER] = socket;
218             EnableButtons();
219          }
220          Update(null);
221          return true;
222       }
223    };
224    Button disconnect
225    {
226       parent = this, text = "Disconnect", position = Point { 170, 370 }, size = Size { 100, 20 }, hotKey = altD;
227       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
228       {
229          if(sockets[SERVER])
230             sockets[SERVER].Disconnect(0);
231          else if(sockets[CLIENT])
232             sockets[CLIENT].Disconnect(0);
233          else if(local)
234          {
235             local = false;
236             gameRunning = false;
237             EnableButtons();
238          }
239          Update(null);
240          return true;
241       }
242    };
243
244    Button stop
245    {
246       parent = this, text = "Stop", position = Point { 80, 330 }, size = Size { 60, 20 }, hotKey = altS;
247       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
248       {
249          hosting = false;
250          service.Stop();
251          EnableButtons();
252          Update(null);
253          return true;
254       }
255    };
256    EditBox address { textHorzScroll = true, line.text = "localhost", parent = this, position = Point{ 60, 370 }, size = Size { 100, 20 } };
257
258    // Window class implementation
259    bool OnCreate()
260    {
261       NewGame();
262       EnableButtons();
263       return true;
264    }
265
266    void OnDestroy()
267    {
268       delete sockets[blue];
269       delete sockets[green];
270    }
271
272    BitmapResource jetonGreen { ":green.bmp", window = this };
273    BitmapResource jetonBlue { ":blue.bmp", window = this };
274    BitmapResource back { ":othello.bmp", window = this };
275
276    void OnRedraw(Surface surface)
277    {
278       // Background...
279       surface.Blit(back.bitmap, 0,0, 0,0, back.bitmap.width, back.bitmap.height);
280       if(gameRunning)
281       {
282          int x,y;
283
284          // Chips...
285          for (y=0; y<NUM_ROWS; y++)
286          {
287             for (x=0; x<NUM_COLUMNS; x++)
288                  {
289                switch(board[y][x])
290                {
291                   case blue:
292                      surface.Blit(jetonBlue.bitmap, x * WIDTH, y * HEIGHT, 0,0, WIDTH-1, HEIGHT-1);
293                      break;
294                   case green:
295                      surface.Blit(jetonGreen.bitmap, x * WIDTH, y * HEIGHT, 0,0, WIDTH-1, HEIGHT-1);
296                      break;
297                }
298             }
299          }
300
301          // Score...
302          if(turn == blue)
303          {
304             surface.SetForeground(white);
305             surface.SetBackground(blue);
306             surface.TextOpacity(true);
307          }
308          else
309          {
310             surface.SetForeground(blue);
311             surface.TextOpacity(false);
312          }
313          surface.WriteTextf(10,  400, "blue: %d", numChips[blue]);
314
315          if(turn == green)
316          {
317             surface.SetForeground(black);
318             surface.SetBackground(green);
319             surface.TextOpacity(true);
320          }
321          else
322          {
323             surface.SetForeground(green);
324             surface.TextOpacity(false);
325          }
326          surface.WriteTextf(210, 400, "green: %d", numChips[green]);
327
328          surface.TextOpacity(false);
329          surface.SetForeground(tomato);
330          if(gameOver)
331             surface.WriteTextf(100, 420, "GAME OVER");
332          else if(!local && sockets[1-turn])
333             surface.WriteTextf(50, 420, "It is your turn to move.");
334       }
335    }
336
337    bool OnKeyDown(Key key, unichar ch)
338    {
339       if(key == escape) Destroy(0);
340       return true;
341    }
342
343    bool OnLeftButtonDown(int x, int y, Modifiers mods)
344    {
345       x /= WIDTH;
346       y /= HEIGHT;
347
348       if(sockets[1-turn] || local)
349       {
350          if(x < NUM_COLUMNS && y < NUM_ROWS && board[y][x] == empty)
351          {
352             OPacket packet { position, turn, (byte)x, (byte)y };
353             if(TurnChips(x, y, turn, 1))
354             {
355                if(!local)
356                   sockets[1-packet.player].Send((byte *)&packet, sizeof(OPacket));
357             }
358          }
359       }
360       return true;
361    }
362 }
363
364 // --- Othello Communication ---
365 class OthelloSocket : Socket
366 {
367    Othello othello;
368    void OnDisconnect(int code)
369    {
370       if(this == othello.sockets[CLIENT])
371       {
372          othello.sockets[CLIENT] = null;
373          othello.gameRunning = false;
374       }
375       else if(this == othello.sockets[SERVER])
376       {
377          othello.sockets[SERVER] = null;
378          othello.gameRunning = false;
379       }
380
381       othello.EnableButtons();
382       othello.Update(null);
383    }
384
385    uint OnReceive(const byte * buffer, uint count)
386    {
387       if(count >= sizeof(OPacket))
388       {
389          OPacket packet = *(OPacket *)buffer;
390          switch(packet.type)
391          {
392             case position:
393                othello.TurnChips(packet.x, packet.y, packet.player, 1);
394                break;
395             case newGame:
396                othello.gameRunning = true;
397                othello.NewGame();
398                break;
399          }
400          return sizeof(OPacket);
401       }
402       return 0;
403    }
404
405    void OnConnect()
406    {
407       othello.sockets[SERVER] = this;
408       othello.gameRunning = true;
409       othello.EnableButtons();
410       othello.NewGame();
411    }
412
413    property Othello othello { set { othello = value; } }
414 }
415
416 class OthelloService : Service
417 {
418    Othello othello;
419    void OnAccept()
420    {
421       if(!othello.sockets[CLIENT] && !othello.gameRunning)
422       {
423          OPacket packet { newGame };
424
425          othello.sockets[CLIENT] = OthelloSocket { this, othello = othello };
426          othello.sockets[CLIENT].Send((byte *)&packet, sizeof(OPacket));
427
428          othello.NewGame();
429          othello.gameRunning = true;
430          othello.EnableButtons();
431          othello.Update(null);
432       }
433    }
434    property Othello othello { set { othello = value; } }
435 }