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