d740aec28948ccb91a0278a195d1de7d7667b3a1
[sdk] / samples / games / crosswords / CrossWordsServer.ec
1 import "crossWords"
2
3 int seed;
4
5 // FOR COMMUNICATION //////////////////////////////////////////
6 struct PlayedTile
7 {
8    int x, y;
9    Letters letter;
10    Letters blankValue;
11
12    void OnSerialize(SerialBuffer buffer)
13    {
14       buffer.Serialize(x);
15       buffer.Serialize(y);
16       buffer.Serialize(letter);
17       buffer.Serialize(blankValue);
18    }
19
20    void OnUnserialize(SerialBuffer buffer)
21    {
22       buffer.Unserialize(x);
23       buffer.Unserialize(y);
24       buffer.Unserialize(letter);
25       buffer.Unserialize(blankValue);
26    }
27 };
28
29 struct PlayedMove
30 {
31    int numTiles;
32    PlayedTile tiles[7];
33    int player;
34    int score;
35
36    void OnSerialize(SerialBuffer buffer)
37    {
38       int c;
39       buffer.Serialize(numTiles);
40       for(c = 0; c<7; c++)
41          buffer.Serialize(tiles[c]);
42       buffer.Serialize(player);
43       buffer.Serialize(score);
44    }
45
46    void OnUnserialize(SerialBuffer buffer)
47    {
48       int c;
49       buffer.Unserialize(numTiles);
50       for(c = 0; c<7; c++)
51          buffer.Unserialize(tiles[c]);
52       buffer.Unserialize(player);
53       buffer.Unserialize(score);
54    }
55 };
56
57 struct PlayerList
58 {
59    char players[4][256];
60    Languages language;
61
62    void OnSerialize(SerialBuffer buffer)
63    {
64       buffer.Serialize(language);
65       buffer.Serialize((StaticString)players[0]);
66       buffer.Serialize((StaticString)players[1]);
67       buffer.Serialize((StaticString)players[2]);
68       buffer.Serialize((StaticString)players[3]);
69    }
70
71    void OnUnserialize(SerialBuffer buffer)
72    {
73       buffer.Unserialize(language);
74       buffer.Unserialize((StaticString)players[0]);
75       buffer.Unserialize((StaticString)players[1]);
76       buffer.Unserialize((StaticString)players[2]);
77       buffer.Unserialize((StaticString)players[3]);
78    }
79 };
80
81 //////////////////////////////////////////
82
83 class Player
84 {
85    Letters letters[7];
86    int numLetters;
87    int totalScore;
88    char * name;
89
90    int id;
91    ServerConnection connection;
92
93    ~Player()
94    {
95       delete name;
96    }
97 };
98
99 static char * dicos[Languages] = { ":englishWords.txt", ":frenchWords.txt" };
100
101 class CrossWordsGame
102 {
103    int lettersAvailable[Letters];
104    int numLettersAvailable;
105    AVLTree<String> dictionary { };
106    int curPlayer;
107    Player players[MaxPlayers];
108    Languages language;
109
110    property Languages language
111    {
112       set
113       {
114          language = value;
115          LoadWords(dicos[value]);
116       }
117    }
118
119    Letters board[15][15];
120    Letters blankValues[15][15];
121    PlayedMove curMove;
122    bool gameStarted;
123
124    ~CrossWordsGame()
125    {
126       int c;
127       dictionary.Free();
128       for(c = 0; c<MaxPlayers; c++)
129          delete players[c];
130    }
131
132    CrossWordsGame()
133    {
134
135    }
136
137    void GetTiles(int tilesPlayer, PlayedMove move)
138    {
139       Player player = players[tilesPlayer];
140       int c;
141       move.numTiles = 0;
142
143       while(player.numLetters < 7 && numLettersAvailable)
144       {
145          Letters letter;
146          int r = (Letters)GetRandom(0, numLettersAvailable-1);
147          int a = 0;
148
149          for(letter = 0; letter<Letters::empty; letter++)
150          {
151             a += lettersAvailable[letter];
152             if(a > r)
153                break;
154          }
155          numLettersAvailable--;
156          lettersAvailable[letter]--;
157
158          player.letters[player.numLetters] = letter;
159          player.numLetters++;
160
161          move.tiles[move.numTiles++].letter = letter;
162       }
163    }
164
165    void NewGame()
166    {
167       int c;
168       int numPlayers = 0;
169
170       for(c = 0; c<MaxPlayers; c++)
171          if(players[c])
172             numPlayers++;
173
174       if(numPlayers > 0)
175       {
176          int x,y;
177          Letters l;
178          PlayerList list { };
179          PlayedMove tiles { };
180
181          numLettersAvailable = 0;
182
183          for(l = 0; l<Letters::empty; l++)
184          {
185             numLettersAvailable += lettersCount[language][l];
186             lettersAvailable[l] = lettersCount[language][l];
187          }
188
189          seed = (uint)(GetTime() * 1000);
190          //seed = 256131322;
191          RandomSeed(seed);
192          //Logf("Seeded with %d\n", seed);
193
194          for(y = 0; y<15; y++)
195             for(x = 0; x<15; x++)
196                board[y][x] = blankValues[y][x] = empty;
197
198
199          for(c = 0; c<MaxPlayers; c++)
200             if(players[c])
201             {
202                curPlayer = c;
203                break;
204             }
205
206          gameStarted = true;
207
208          strcpy(list.players[0], crossWordsGame.players[0] ? crossWordsGame.players[0].name : "");
209          strcpy(list.players[1], crossWordsGame.players[1] ? crossWordsGame.players[1].name : "");
210          strcpy(list.players[2], crossWordsGame.players[2] ? crossWordsGame.players[2].name : "");
211          strcpy(list.players[3], crossWordsGame.players[3] ? crossWordsGame.players[3].name : "");
212          list.language = crossWordsGame.language;
213
214          for(c = 0; c<MaxPlayers; c++)
215             if(players[c])
216             {
217                crossWordsGame.GetTiles(c, tiles);
218                crossWordsGame.players[c].connection.GameStarted(list, tiles);
219             }
220       }
221    }
222
223    void LoadWords(char * fileName)
224    {
225       File f = FileOpen(fileName, read);
226       if(f)
227       {
228          dictionary.Free();
229          while(!f.Eof())
230          {
231             char word[100];
232             f.GetLine(word, sizeof(word));
233             if(word[0])
234             {
235                String s = CopyString(word);
236                if(!dictionary.Add(CopyString(word)))
237                   delete s;
238             }
239          }
240          delete f;
241       }
242
243       /*{
244          IteratorPointer test1, test2;
245          test1 = dictionary.Find("gewurztraminer");
246          test2 = dictionary.Find("gewurstraminer");
247          Print("");
248       }*/
249    }
250
251    bool CheckWord(Letters newBoard[15][15], Direction direction, int where, int start, int end, int * score)
252    {
253       char word[16];
254       int c, len = 0;
255       bool found;
256       int wordScore = 0;
257       int wordMultiplier = 1;
258
259       for(c = start; c<= end; c++)
260       {
261          int y = direction ? c : where;
262          int x = direction ? where : c;
263          Letters letter = newBoard[y][x];
264          int multiplier = 1;
265
266          if(board[y][x] == empty)
267          {
268             if(squareValues[y][x] == doubleLetter) multiplier = 2;
269             else if(squareValues[y][x] == tripleLetter) multiplier = 3;
270          }
271
272          wordScore += lettersValue[language][letter] * multiplier;
273
274          if(board[y][x] == empty)
275          {
276             if(squareValues[y][x] == doubleWord) wordMultiplier *= 2;
277             else if(squareValues[y][x] == tripleWord) wordMultiplier *= 3;
278          }
279
280          if(letter == blank)
281             letter = blankValues[y][x];
282          word[len++] = (char)letter + 'a';
283       }
284       word[len] = 0;
285       found = dictionary.Find(word) != null;
286       Logf("%s: %s\n", word, found ? "found :)" : "not found :(");
287
288       *score += wordScore * wordMultiplier;
289       return found;
290    }
291
292    bool VerifyMove(Letters newBoard[15][15], int * score)
293    {
294       int x, y;
295       int c;
296       int numLetters = 0;
297       Direction direction = horizontal;
298
299       int wordX;
300       int wordY;
301       int firstX = MAXINT, lastX = MININT;
302       int firstY = MAXINT, lastY = MININT;
303       int first, last, where;
304       int wordStart, wordEnd;
305       bool anchored = false;
306
307       *score = 0;
308
309       for(y = 0; y<15; y++)
310       {
311          for(x = 0; x<15; x++)
312          {
313             if(board[y][x] == empty && newBoard[y][x] != empty)
314             {
315                wordX = x;
316                wordY = y;
317                if(x < firstX) firstX = x;
318                if(y < firstY) firstY = y;
319                if(x > lastX) lastX = x;
320                if(y > lastY) lastY = y;
321                numLetters++;
322             }
323          }
324       }
325       if(!numLetters) return false;
326       if(lastY - firstY > 0)
327       {
328          if(lastX - firstX > 1) return false;
329          direction = vertical;
330       }
331       else if(lastX - firstX == 0)
332       {
333          if(((firstY > 0 && newBoard[firstY-1][firstX] != empty) ||
334             (firstY < 14 && newBoard[firstY+1][firstX] != empty)) &&
335             ((firstX == 0 || newBoard[firstY][firstX-1] == empty) &&
336             (firstX == 14 || newBoard[firstY][firstX+1] == empty)))
337             direction = vertical;
338       }
339
340       first = direction ? firstY : firstX;
341       last = direction ? lastY : lastX;
342
343       where = direction ? firstX : firstY;
344
345       for(c = first; c <= last; c++)
346       {
347          Letters letter = direction ? newBoard[c][where] : newBoard[where][c];
348          if(letter == empty) return false;
349       }
350
351       for(c = first; c>=0; c--)
352       {
353          Letters letter = direction ? newBoard[c][where] : newBoard[where][c];
354          if(letter == empty) break;
355          wordStart = c;
356          if(c != first)
357             anchored = true;
358       }
359       for(c = last; c<15; c++)
360       {
361          Letters letter = direction ? newBoard[c][where] : newBoard[where][c];
362          if(letter == empty) break;
363          wordEnd = c;
364          if(c != last)
365             anchored = true;
366       }
367       if(!CheckWord(newBoard, direction, where, wordStart, wordEnd, score))
368          return false;
369
370       for(c = first; c <= last; c++)
371       {
372          if((direction ? board[c][where] : board[where][c]) == empty)
373          {
374             int wordStart, wordEnd;
375             int a;
376
377             for(a = where; a>=0; a--)
378             {
379                Letters letter = direction ? newBoard[c][a] : newBoard[a][c];
380                if(letter == empty) break;
381                wordStart = a;
382                if(a != where)
383                   anchored = true;
384             }
385             for(a = where; a<15; a++)
386             {
387                Letters letter = direction ? newBoard[c][a] : newBoard[a][c];
388                if(letter == empty) break;
389                wordEnd = a;
390                if(a != where)
391                   anchored = true;
392             }
393             if(wordEnd > wordStart)
394                if(!CheckWord(newBoard, direction^1, c, wordStart, wordEnd, score))
395                   return false;
396          }
397          else
398             anchored = true;
399       }
400       if(newBoard[7][7] != empty && board[7][7] == empty) anchored = true;
401       if(!anchored) return false;
402       if(numLetters == 7) *score += 50;
403       return true;
404    }
405
406    void EndGame()
407    {
408       if(gameStarted)
409       {
410          int c;
411          for(c = 0; c<MaxPlayers; c++)
412          {
413             delete crossWordsGame.players[c];
414          }
415          gameStarted = false;
416       }
417    }
418 }
419
420 CrossWordsGame crossWordsGame;
421
422 remote class ServerConnection
423 {
424 public:
425    // Remote Functions
426    Player player;
427
428    ~ServerConnection()
429    {
430       if(player)
431       {
432          //crossWordsGame.players[player.id] = null;
433          delete crossWordsGame.players[player.id];
434          delete player;
435          crossWordsGame.EndGame();
436       }
437    }
438
439    int Join()
440    {
441       int result = -1;
442       if(!crossWordsGame.gameStarted)
443       {
444          int c;
445          for(c = 0; c<MaxPlayers; c++)
446          {
447             if(!crossWordsGame.players[c])
448                break;
449          }
450          if(c < MaxPlayers)
451          {
452             player = crossWordsGame.players[c] = Player { id = c };
453             player.connection = this;
454             incref player;
455             incref crossWordsGame.players[c];
456             result = c;
457          }
458       }
459       return result;
460    }
461
462    bool SetName(const String name)
463    {
464       if(player)
465       {
466          player.name = CopyString(name);
467          if(!crossWords.scoreFields[player.id])
468          {
469             crossWords.scoreFields[player.id] = DataField { dataType = class(int), header = name, width = 40 };
470             //incref crossWords.scoreFields[player.id];
471             crossWords.scores.AddField(crossWords.scoreFields[player.id]);
472          }
473          crossWords.scoreFields[player.id].header = name;
474       }
475       return true;
476    }
477
478    bool PlayTiles(PlayedMove move)
479    {
480       if(player && crossWordsGame.players[crossWordsGame.curPlayer] == player)
481       {
482          Letters newBoard[15][15];
483          int c;
484          int score;
485
486          memcpy(newBoard, crossWordsGame.board, 15*15*sizeof(Letters));
487          for(c = 0; c<move.numTiles; c++)
488          {
489             if(newBoard[move.tiles[c].y][move.tiles[c].x] == empty)
490             {
491                newBoard[move.tiles[c].y][move.tiles[c].x] = move.tiles[c].letter;
492                crossWordsGame.blankValues[move.tiles[c].y][move.tiles[c].x] = move.tiles[c].blankValue;
493             }
494             else
495                return false;
496          }
497          if(crossWordsGame.VerifyMove(newBoard, &score))
498          {
499             for(c = 0; c<move.numTiles; c++)
500             {
501                int d;
502                bool found = false;
503                for(d = 0; d<player.numLetters; d++)
504                   if(player.letters[d] == move.tiles[c].letter)
505                   {
506                      memmove(player.letters + d, player.letters + d + 1, sizeof(int) * (player.numLetters - d - 1));
507                      player.numLetters--;
508                      found = true;
509                      break;
510                   }
511                if(!found)
512                {
513                   Logf("Letter not found!\n");
514                   return false;
515                }
516             }
517
518             player.totalScore += score;
519             memcpy(crossWordsGame.board, newBoard, 15*15*sizeof(Letters));
520
521             crossWordsGame.curMove = move;
522             crossWordsGame.curMove.player = crossWordsGame.curPlayer;
523             crossWordsGame.curMove.score = score;
524
525             // Return Tiles
526             crossWordsGame.GetTiles(crossWordsGame.curPlayer, move);
527
528             for(c = 0; c<MaxPlayers; c++)
529                if(crossWordsGame.players[c])
530                   crossWordsGame.players[c].connection.MovePlayed(crossWordsGame.curMove);
531
532             do { if(++crossWordsGame.curPlayer == MaxPlayers) crossWordsGame.curPlayer = 0; } while(!crossWordsGame.players[crossWordsGame.curPlayer]);
533             return true;
534          }
535       }
536       return false;
537    }
538
539    bool DiscardTiles(PlayedMove move)
540    {
541       if(player && crossWordsGame.numLettersAvailable >= move.numTiles && crossWordsGame.players[crossWordsGame.curPlayer] == player)
542       {
543          int c;
544          PlayedMove newTiles { };
545
546          for(c = 0; c<move.numTiles; c++)
547          {
548             int d;
549             for(d = 0; d<player.numLetters; d++)
550                if(player.letters[d] == move.tiles[c].letter)
551                {
552                   memmove(player.letters + d, player.letters + d + 1, sizeof(int) * (player.numLetters - d - 1));
553                   player.numLetters--;
554                   break;
555                }
556          }
557
558          crossWordsGame.GetTiles(crossWordsGame.curPlayer, newTiles);
559
560          for(c = 0; c<move.numTiles; c++)
561          {
562             crossWordsGame.lettersAvailable[move.tiles[c].letter]++;
563             crossWordsGame.numLettersAvailable++;
564          }
565
566          crossWordsGame.curMove = { }; // TODO: Add informing other players of number of exchanged letters when passing...
567          crossWordsGame.curMove.player = crossWordsGame.curPlayer;
568
569          move = newTiles;
570
571          for(c = 0; c<MaxPlayers; c++)
572             if(crossWordsGame.players[c])
573                crossWordsGame.players[c].connection.MovePlayed(crossWordsGame.curMove);
574
575          do { if(++crossWordsGame.curPlayer == MaxPlayers) crossWordsGame.curPlayer = 0; } while(!crossWordsGame.players[crossWordsGame.curPlayer]);
576
577          return true;
578       }
579       return false;
580    }
581
582    virtual void MovePlayed(PlayedMove move);
583    virtual void GameStarted(PlayerList list, PlayedMove tiles);
584 }