cleaned all trailing white space from source files.
[sdk] / samples / games / scrabble / scrabble.ec
1 #ifdef STATIC
2 import static "ecere"
3 #else
4 import "ecere"
5 #endif
6
7 import remote "ScrabbleServer"
8
9 define TileSize = 32;
10
11 enum SquareValue
12 {
13    normal,
14    doubleLetter, dl = doubleLetter,
15    tripleLetter, tl = tripleLetter,
16    doubleWord,   dw = doubleWord,
17    tripleWord,   tw = tripleWord
18 };
19 enum Direction
20 {
21    horizontal,
22    vertical
23 };
24
25 define TrayHeight = 550;
26 define TrayLeft = 50;
27 define TrayWidth = 300;
28
29 define ThrowHeight = 620;
30
31 define BoardX = 20;
32 define BoardY = 20;
33
34 define MaxPlayers = 4;
35
36 SquareValue squareValues[15][15] =
37 {
38    { tw, 0, 0,dl, 0, 0, 0,tw, 0, 0, 0,dl, 0, 0,tw },
39    {  0,tw, 0, 0, 0,tl, 0, 0, 0,tl, 0, 0, 0,tw, 0 },
40    {  0, 0,tw, 0, 0, 0,dl, 0,dl, 0, 0, 0,tw, 0, 0 },
41    { dl, 0, 0,tw, 0, 0, 0,dl, 0, 0, 0,tw, 0, 0,dl },
42    {  0, 0, 0, 0,tw, 0, 0, 0, 0, 0,tw, 0, 0, 0, 0 },
43    {  0,tl, 0, 0, 0,tl, 0, 0, 0,tl, 0, 0, 0,tl, 0 },
44    {  0, 0,dl, 0, 0, 0,dl, 0,dl, 0, 0, 0,dl, 0, 0 },
45    { tw, 0, 0,dl, 0, 0, 0,tw, 0, 0, 0,dl, 0, 0,tw },
46    {  0, 0,dl, 0, 0, 0,dl, 0,dl, 0, 0, 0,dl, 0, 0 },
47    {  0,tl, 0, 0, 0,tl, 0, 0, 0,tl, 0, 0, 0,tl, 0 },
48    {  0, 0, 0, 0,tw, 0, 0, 0, 0, 0,tw, 0, 0, 0, 0 },
49    { dl, 0, 0,tw, 0, 0, 0,dl, 0, 0, 0,tw, 0, 0,dl },
50    {  0, 0,tw, 0, 0, 0,dl, 0,dl, 0, 0, 0,tw, 0, 0 },
51    {  0,tw, 0, 0, 0,tl, 0, 0, 0,tl, 0, 0, 0,tw, 0 },
52    { tw, 0, 0,dl, 0, 0, 0,tw, 0, 0, 0,dl, 0, 0,tw }
53 };
54 Color valueColors[SquareValue] =
55 {
56    0, skyBlue, slateBlue, pink, red
57 };
58
59 enum Letters
60 {
61    a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z, blank, empty
62 };
63 enum Languages
64 {
65    english, french
66 };
67
68 // FRANCAIS
69 int lettersCount[Languages][Letters] =
70 {
71    {
72       9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1,   2, 0
73    },
74    {
75       9, 2, 2, 3, 15, 2, 2, 2, 8, 1, 1, 5, 3, 6, 6, 2, 1, 6, 6, 6, 6, 2, 1, 1, 1, 1,   2, 0
76    }
77 };
78
79 int lettersValue[Languages][Letters] =
80 {
81    {
82       1, 3, 3, 2, 1,  4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3,10, 1, 1, 1, 1, 4, 4, 8, 4, 10,  0, 0
83    },
84    {
85       1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 10, 1, 2, 1, 1, 3, 8, 1, 1, 1, 1, 4, 10, 10, 10, 10,  0, 0
86    }
87 };
88
89 Scrabble scrabble {};
90
91 class DefineBlank : Window
92 {
93    text = "Please choose the value of your blank tile";
94    size = { 250, 480 };
95    borderStyle = fixed;
96    ListBox list
97    {
98       this;
99       position = { 20, 20 };
100       size = { 80, 420 };
101
102       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
103       {
104          if(list.currentRow) button.disabled = false;
105          return true;
106       }
107
108       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
109       {
110          if(!button.disabled)
111             button.NotifyClicked(this, button, 0,0,0);
112          return true;
113       }
114    };
115    DataField field { class(Letters) };
116    Button button
117    {
118       this;
119       text = "Choose";
120       size = { 80 };
121       isDefault = true;
122       position = { 130, 420 };
123       disabled = true;
124
125       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
126       {
127          Destroy((DialogResult)(Letters)list.currentRow.GetData(field));
128          return true;
129       }
130    };
131
132    DefineBlank()
133    {
134       Letters c;
135       list.AddField(field);
136       for(c = 0; c<Letters::blank; c++)
137       {
138          list.AddRow().SetData(field, c);
139       }
140    }
141 }
142
143 class Scrabble : Window
144 {
145    Letters boardLetters[15][15];
146    Letters lastBoard[15][15];
147    Letters blankValues[15][15];
148    bool gameStarted;
149    int turn;
150    int playerID;
151    Languages language;
152
153    Letters throwLetters[7];
154    int numThrowLetters;
155
156    bool dragging;
157    int dragLetter;
158    int dragY, dragX;
159    int startX, startY;
160    int moveX, moveY;
161    bool out;
162    int lastX;
163    int dragPos;
164
165    ServerConnection server;
166
167    Letters letters[7];
168    int numLetters;
169    int positions[7];
170    char playerNames[MaxPlayers][256];
171    int playerScores[MaxPlayers];
172
173    int totalScore;
174
175
176    text = "Ecere Scrabble";
177    borderStyle = sizable;
178    hasMaximize = true;
179    hasMinimize = true;
180    hasClose = true;
181    size = { 700, 700 };
182    background = green;
183
184    FontResource letterFont { "Arial", 17, true, window = this };
185    FontResource valueFont { "Arial", 8, true, window = this  };
186    FontResource scoreFont { "Arial", 6, true, window = this  };
187    FontResource wingdings { "Wingdings", 30, true, window = this };
188
189    bool OnClose(bool parentClosing)
190    {
191       return !gameStarted || MessageBox { master = this, type = yesNo, text = "Ecere Scrabble", contents = "Are you sure you want to quit?" }.Modal() == yes;
192    }
193
194    bool OnCreate()
195    {
196       int x,y;
197       for(y = 0; y<15; y++)
198          for(x = 0; x<15; x++)
199             lastBoard[y][x] = blankValues[y][x] = boardLetters[y][x] = empty;
200       return true;
201    }
202
203    void DrawLetter(Surface surface, Letters a, int x, int y, bool blank)
204    {
205       if(!blank)
206       {
207          surface.SetBackground(sandyBrown);
208          surface.Area(x+2, y+2, x + TileSize-1, y + TileSize-1);
209       }
210       if(a != Letters::blank)
211       {
212          char ch = (char)a + 'A';
213          surface.SetForeground(blank ? gray : black);
214          surface.TextFont(letterFont.font);
215          surface.WriteText(x + 5, y, &ch, 1);
216
217          if(!blank)
218          {
219             surface.TextFont(valueFont.font);
220             surface.WriteTextDotsf(right, x, y + TileSize - 14, TileSize+1, "%d", lettersValue[language][a]);
221          }
222       }
223    }
224
225    void DrawSquare(Surface surface, int cx, int cy)
226    {
227       SquareValue value = squareValues[cy][cx];
228       Color color = valueColors[value];
229       int num = (value == doubleLetter || value == doubleWord) ? 2 : 3;
230       int x = cx * TileSize + BoardX;
231       int y = cy * TileSize + BoardY;
232
233       if(boardLetters[cy][cx] != empty)
234       {
235          DrawLetter(surface, boardLetters[cy][cx], x, y, false);
236          if(boardLetters[cy][cx] == blank && blankValues[cy][cx] != empty)
237             DrawLetter(surface, blankValues[cy][cx], x, y, true);
238       }
239       if(value)
240       {
241          int c;
242          surface.SetBackground(color);
243          surface.SetForeground(color);
244
245          for(c = 0; c<num; c++)
246          {
247             int a = TileSize * (c+1) / (num+1);
248             surface.PutPixel(x + a, y);
249             surface.PutPixel(x + a+1, y);
250             surface.PutPixel(x + a-1, y+1);
251             surface.PutPixel(x + a, y+1);
252             surface.PutPixel(x + a+1, y+1);
253             surface.PutPixel(x + a+2, y+1);
254
255             surface.PutPixel(x, y + a);
256             surface.PutPixel(x, y + a+1);
257             surface.PutPixel(x+1, y + a-1);
258             surface.PutPixel(x+1, y + a);
259             surface.PutPixel(x+1, y + a+1);
260             surface.PutPixel(x+1, y + a+2);
261
262             surface.PutPixel(x + a, y+TileSize+1);
263             surface.PutPixel(x + a+1, y+TileSize+1);
264             surface.PutPixel(x + a-1, y+TileSize);
265             surface.PutPixel(x + a, y+TileSize);
266             surface.PutPixel(x + a+1, y+TileSize);
267             surface.PutPixel(x + a+2, y+TileSize);
268
269             surface.PutPixel(x+TileSize+1, y + a);
270             surface.PutPixel(x+TileSize+1, y + a+1);
271             surface.PutPixel(x+TileSize, y + a-1);
272             surface.PutPixel(x+TileSize, y + a);
273             surface.PutPixel(x+TileSize, y + a+1);
274             surface.PutPixel(x+TileSize, y + a+2);
275          }
276
277          if(boardLetters[cy][cx] == empty)
278          {
279             surface.Area(x + 2, y + 2, x + TileSize - 1, y + TileSize - 1);
280             surface.SetForeground(black);
281             if(cx == 7 && cy == 7)
282             {
283                unichar star = 0xAB;
284                char utf8[4];
285                int len = UTF32toUTF8Len(&star, 1, utf8, 4);
286                surface.TextFont(wingdings.font);
287                surface.WriteText(x - 1, y - 4, utf8, len);
288             }
289             else
290             {
291                surface.TextFont(scoreFont.font);
292                if(language == french)
293                {
294                   surface.CenterTextf(x + TileSize/2+1, y + TileSize / 4 - 5, (value == doubleLetter || value == tripleLetter) ? "Lettre" : "Mot");
295                   surface.CenterTextf(x + TileSize/2+1, y + TileSize * 2 / 4 - 5, "Compte");
296                   surface.CenterTextf(x + TileSize/2+1, y + TileSize * 3 / 4 - 5, (value == doubleLetter || value == doubleWord) ? "Double" : "Triple");
297                }
298                else
299                {
300                   surface.CenterTextf(x + TileSize/2+1, y + TileSize / 4 - 5, (value == doubleLetter || value == doubleWord) ? "Double" : "Triple");
301                   surface.CenterTextf(x + TileSize/2+1, y + TileSize * 2 / 4 - 5, (value == doubleLetter || value == tripleLetter) ? "Letter" : "Word");
302                   surface.CenterTextf(x + TileSize/2+1, y + TileSize * 3 / 4 - 5, "Score");
303                }
304             }
305          }
306       }
307       else if(boardLetters[cy][cx] == empty)
308       {
309          surface.SetBackground(burlyWood);
310          surface.Area(x + 2, y + 2, x + TileSize - 1, y + TileSize - 1);
311       }
312    }
313
314    void OnRedraw(Surface surface)
315    {
316       int c;
317       int x, y;
318       surface.SetForeground(white);
319       for(y = 0; y<16; y++)
320       {
321          surface.HLine(BoardX, BoardX + TileSize * 15, BoardY + y * TileSize);
322          surface.HLine(BoardX, BoardX + TileSize * 15, BoardY + y * TileSize+1);
323       }
324
325       for(x = 0; x<16; x++)
326       {
327          surface.VLine(BoardY + 0, BoardY + TileSize * 15, BoardX + x * TileSize);
328          surface.VLine(BoardY + 0, BoardY + TileSize * 15, BoardX + x * TileSize+1);
329       }
330       for(y = 0; y<15; y++)
331          for(x = 0; x<15; x++)
332          {
333             DrawSquare(surface, x, y);
334          }
335
336       for(c = 0; c<numLetters; c++)
337       {
338          if(dragging && c == dragLetter)
339             DrawLetter(surface, letters[c], TrayLeft + moveX, TrayHeight - moveY, false);
340          else
341             DrawLetter(surface, letters[c], TrayLeft + positions[c], TrayHeight, false);
342       }
343
344       for(c = 0; c<numThrowLetters; c++)
345       {
346          DrawLetter(surface, throwLetters[c], TrayLeft + c * TileSize, ThrowHeight, false);
347       }
348
349       if(gameStarted && turn == playerID)
350       {
351          surface.SetForeground(red);
352          surface.TextFont(letterFont.font);
353          surface.WriteTextf(540, 400, "Your turn");
354       }
355    }
356
357    bool OnLeftButtonDown(int mx, int my, Modifiers mods)
358    {
359       int c;
360
361       // Picking Letters on the board
362       if(gameStarted && turn == playerID)
363       {
364          if(mx > BoardX && mx < BoardX + TileSize * 15 &&
365             my > BoardY && my < BoardY + TileSize * 15)
366          {
367             int x = (mx - BoardX) / TileSize;
368             int y = (my - BoardY) / TileSize;
369             if(boardLetters[y][x] != empty && lastBoard[y][x] == empty)
370             {
371                letters[numLetters] = boardLetters[y][x];
372                dragLetter = numLetters;
373
374                boardLetters[y][x] = empty;
375                blankValues[y][x] = empty;
376                numLetters++;
377                dragging = true;
378
379                dragY = my;
380                dragPos = -1;
381                Capture();
382                out = true;
383
384                moveX = x * TileSize + BoardX - TrayLeft;
385                moveY = TrayHeight - BoardY - y * TileSize;
386
387                startX = moveX;
388                startY = moveY;
389                lastX = moveX;
390                dragX = mx;
391             }
392          }
393       }
394
395       // Picking letters in your hand
396       for(c = 0; c < numLetters; c++)
397       {
398          int x1 = TrayLeft + positions[c];
399          int y1 = TrayHeight;
400          if(mx >= x1 && my >= y1 && mx < x1 + TileSize && my < y1 + TileSize)
401          {
402             dragging = true;
403             dragY = my;
404             dragX = mx;
405             moveY = 0;
406             startY = 0;
407             moveX = positions[c];
408             startX = positions[c];
409             lastX = positions[c];
410             dragLetter = c;
411             dragPos = -1;
412             out = false;
413
414             Capture();
415             break;
416          }
417       }
418
419       // Picking letters to throw
420       if(my > ThrowHeight && mx >= TrayLeft && mx < TrayLeft + numThrowLetters * TileSize)
421       {
422          int c = (mx - TrayLeft) / TileSize;
423          letters[numLetters] = throwLetters[c];
424          dragLetter = numLetters;
425
426          memmove(throwLetters + c, throwLetters + c + 1, sizeof(int) * (numThrowLetters - c - 1));
427          numThrowLetters--;
428
429          numLetters++;
430          dragging = true;
431
432          dragY = my;
433          dragPos = -1;
434          Capture();
435          out = true;
436
437          moveX = c * TileSize;
438          moveY = TrayHeight - ThrowHeight;
439
440          startX = moveX;
441          startY = moveY;
442          lastX = moveX;
443          dragX = mx;
444       }
445       return true;
446    }
447
448    int ::ComparePositions(int * a, int * b)
449    {
450       int posA = (*a == scrabble.dragLetter) ? scrabble.moveX : scrabble.positions[*a];
451       int posB = (*b == scrabble.dragLetter) ? scrabble.moveX : scrabble.positions[*b];
452
453       if(posA < posB)
454          return -1;
455       else if(posA > posB)
456          return 1;
457       else
458          return 0;
459    }
460
461    bool OnMouseMove(int mx, int my, Modifiers mods)
462    {
463       if(dragging)
464       {
465          int x;
466          int y;
467          int ordered[7];
468          int c;
469          bool collision = false;
470
471          y = startY + dragY - my;
472
473          x = startX + mx - dragX;
474          moveX = x;
475
476          if(y < 0 && y > -TileSize && moveX >= TrayLeft && moveX < TrayLeft + TrayWidth) y = 0;
477
478          moveY = y;
479
480          for(c = 0; c<numLetters; c++)
481             ordered[c] = c;
482
483          if(y < TileSize && y > -TileSize && moveX + TileSize >= 0 && moveX <= TrayWidth)
484          {
485             qsort(ordered, numLetters, sizeof(int), ComparePositions);
486
487             if(!out && dragPos != -1 && ordered[dragPos] != dragLetter)
488             {
489                int pos;
490                int oldValue = ordered[dragPos];
491                for(c = 0; c<numLetters; c++)
492                   if(ordered[c] == dragLetter)
493                   {
494                      pos = c;
495                      break;
496                   }
497                if(pos < dragPos)
498                {
499                   memmove(ordered + pos, ordered + pos + 1, (dragPos - pos) * sizeof(int));
500                   ordered[dragPos] = dragLetter;
501                }
502                else
503                {
504                   memmove(ordered + dragPos + 1, ordered + dragPos, (pos - dragPos) * sizeof(int));
505                   ordered[dragPos] = dragLetter;
506                }
507
508                ordered[dragPos] = dragLetter;
509                ordered[pos] = oldValue;
510             }
511
512             if(moveX + TileSize >= 0 && moveX <= TrayWidth)
513             {
514                for(c = 0; c<numLetters; c++)
515                {
516                   if(c != dragLetter)
517                   {
518                      if(positions[c] + TileSize > moveX &&
519                         positions[c] < moveX + TileSize)
520                         break;
521                   }
522                }
523                // COLLISION! (Insert in between?)
524                if(out && c != numLetters)
525                   collision = true;
526             }
527
528             if(moveX >= lastX || collision)
529             {
530                int end;
531
532                for(c = 0; c<numLetters; c++)
533                {
534                   if(ordered[c] == dragLetter)
535                   {
536                      c++;
537                      break;
538                   }
539                }
540
541                end = moveX + TileSize;
542
543                for(; c < numLetters; c++)
544                {
545                   if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) < end)
546                   {
547                      if(ordered[c] != dragLetter)
548                         positions[ordered[c]] = end;
549                      else
550                         moveX = end;
551                   }
552                   end = ((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize;
553                }
554
555                // RIGHT LIMIT
556                end = TrayWidth;
557                for(c = numLetters-1; c>= 0; c--)
558                {
559                   if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize > end)
560                   {
561                      if(ordered[c] != dragLetter)
562                         positions[ordered[c]] = end - TileSize;
563                      else
564                         moveX = end - TileSize;
565                   }
566                   end = (ordered[c] == dragLetter) ? moveX : positions[ordered[c]];
567                }
568             }
569             if(moveX < lastX || collision)
570             {
571                int end;
572                for(c = numLetters-1; c>=0; c--)
573                {
574                   if(ordered[c] == dragLetter)
575                   {
576                      c--;
577                      break;
578                   }
579                }
580
581                end = moveX;
582                for(; c >= 0; c--)
583                {
584                   if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize > end)
585                   {
586                      if(ordered[c] != dragLetter)
587                         positions[ordered[c]] = end - TileSize;
588                      else
589                         moveX = end - TileSize;
590                   }
591                   end = (ordered[c] == dragLetter) ? moveX : positions[ordered[c]];
592                }
593
594                // LEFT LIMIT
595                end = 0;
596                for(c = 0; c<numLetters; c++)
597                {
598                   if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) < end)
599                   {
600                      if(dragLetter != ordered[c])
601                         positions[ordered[c]] = end;
602                      else
603                         moveX = end;
604                   }
605                   end = ((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize;
606                }
607             }
608          }
609
610          qsort(ordered, numLetters, sizeof(int), ComparePositions);
611          out = moveY >= TileSize || moveY < -TileSize;
612          lastX = moveX;
613
614          for(c = 0; c<numLetters; c++)
615             if(ordered[c] == dragLetter)
616             {
617                dragPos = c;
618                break;
619             }
620
621          // Dropping On the board
622          if(gameStarted && turn == playerID)
623          {
624             if(mx > BoardX && mx < BoardX + TileSize * 15 &&
625                my > BoardY && my < BoardY + TileSize * 15)
626             {
627                x = (mx - BoardX) / TileSize;
628                y = (my - BoardY) / TileSize;
629                if(boardLetters[y][x] == empty)
630                {
631                   moveX = x * TileSize + BoardX - TrayLeft;
632                   moveY = TrayHeight - BoardY - y * TileSize;
633                }
634             }
635          }
636          Update(null);
637       }
638       return true;
639    }
640
641    void FixLetters()
642    {
643       int end;
644       int ordered[7];
645       int c;
646
647       for(c = 0; c<numLetters; c++)
648          ordered[c] = c;
649       qsort(ordered, numLetters, sizeof(int), ComparePositions);
650
651       end = 0;
652
653       for(c = 0; c < numLetters; c++)
654       {
655          if(positions[ordered[c]] < end)
656             positions[ordered[c]] = end;
657          end = positions[ordered[c]] + TileSize;
658       }
659
660       // RIGHT LIMIT
661       end = TrayWidth;
662       for(c = numLetters-1; c>= 0; c--)
663       {
664          if(positions[ordered[c]] + TileSize > end)
665             positions[ordered[c]] = end - TileSize;
666          end = positions[ordered[c]];
667       }
668    }
669
670    bool OnLeftButtonUp(int mx, int my, Modifiers mods)
671    {
672       if(dragging)
673       {
674          dragging = false;
675          ReleaseCapture();
676
677          if(my > ThrowHeight)
678          {
679             throwLetters[numThrowLetters++] = letters[dragLetter];
680
681             memmove(letters + dragLetter, letters + dragLetter + 1, sizeof(int) * (numLetters - dragLetter - 1));
682             memmove(positions + dragLetter, positions + dragLetter + 1, sizeof(int) * (numLetters - dragLetter - 1));
683             numLetters--;
684             dragLetter = -1;
685          }
686
687          // Dropping Letters on the board
688          if(gameStarted && turn == playerID)
689          {
690             if(mx > BoardX && mx < BoardX + TileSize * 15 &&
691                my > BoardY && my < BoardY + TileSize * 15)
692             {
693                int x = (mx - BoardX) / TileSize;
694                int y = (my - BoardY) / TileSize;
695                if(boardLetters[y][x] == empty)
696                {
697                   boardLetters[y][x] = letters[dragLetter];
698                   memmove(letters + dragLetter, letters + dragLetter + 1, sizeof(int) * (numLetters - dragLetter - 1));
699                   memmove(positions + dragLetter, positions + dragLetter + 1, sizeof(int) * (numLetters - dragLetter - 1));
700                   numLetters--;
701                   dragLetter = -1;
702                   if(boardLetters[y][x] == blank)
703                   {
704                      DefineBlank dialog { master = this };
705                      blankValues[y][x] = (Letters)dialog.Modal();
706                   }
707                }
708             }
709          }
710
711          if(dragLetter != -1)
712          {
713             positions[dragLetter] = moveX;
714             dragLetter = -1;
715             FixLetters();
716          }
717
718          Update(null);
719       }
720       return true;
721    }
722
723    DataField scoreFields[MaxPlayers];
724
725    Button playBtn
726    {
727       this, text = "Play", bevel = false, position = { 520, 540 };
728       font = { "Arial", 20, true };
729       opacity = 0; drawBehind = true;
730
731       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
732       {
733          if(!numThrowLetters && gameStarted && turn == playerID)
734          {
735             int x, y;
736             PlayedMove move { };
737             for(y = 0; y<15; y++)
738             {
739                for(x = 0; x<15; x++)
740                {
741                   if(lastBoard[y][x] == empty && boardLetters[y][x] != empty)
742                   {
743                      move.tiles[move.numTiles].x = x;
744                      move.tiles[move.numTiles].y = y;
745                      move.tiles[move.numTiles].letter = boardLetters[y][x];
746                      move.tiles[move.numTiles].blankValue = blankValues[y][x];
747                      move.numTiles++;
748                   }
749                }
750             }
751             if(move.numTiles <= 7 && move.numTiles > 0)
752             {
753                if(server.PlayTiles(move))
754                {
755                   memcpy(lastBoard, boardLetters, sizeof(lastBoard));
756                   AddLetters(move);
757                   Update(null);
758                }
759             }
760          }
761          return true;
762       }
763    };
764
765    void AddLetters(PlayedMove move)
766    {
767       int end = 0;
768       int c;
769
770       for(c = 0; c<numLetters; c++)
771          if(positions[c] + TileSize > end)
772             end = positions[c] + TileSize;
773
774       for(c = 0; c<move.numTiles; c++)
775       {
776          letters[numLetters] = move.tiles[c].letter;
777          positions[numLetters] = end;
778          end += TileSize;
779          numLetters++;
780       }
781       FixLetters();
782    }
783
784    Button dumpBtn
785    {
786       this, text = "Throw / Pass", bevel = false, position = { 430, 622 };
787       font = { "Arial", 20, true };
788       opacity = 0; drawBehind = true;
789
790       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
791       {
792          bool placed = false;
793          for(y = 0; y<15; y++)
794             for(x = 0; x<15; x++)
795                if(boardLetters[y][x] != lastBoard[y][x])
796                   placed = true;
797
798          if(!placed && gameStarted && turn == playerID)
799          {
800             int c;
801             PlayedMove move { };
802             for(c = 0; c<numThrowLetters; c++)
803             {
804                move.tiles[move.numTiles].letter = throwLetters[c];
805                move.numTiles++;
806             }
807
808             if(server.DiscardTiles(move))
809             {
810                numThrowLetters = 0;
811                AddLetters(move);
812                Update(null);
813             }
814          }
815          return true;
816       }
817    };
818    Button connectBtn
819    {
820       this, text = "Connect", bevel = false, position = { 520, 122 };
821       font = { "Arial", 20, true };
822       opacity = 0; drawBehind = true;
823
824       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
825       {
826          if(!server)
827          {
828             server = ServerConnection
829             {
830                void OnDisconnect(int code)
831                {
832                   if(scrabble)
833                   {
834                      delete scrabble.server;
835                      scrabble.gameStarted = false;
836                      scrabble.Update(null);
837                   }
838                   DCOMClientObject::OnDisconnect(code);
839                }
840
841                void GameStarted(PlayerList list, PlayedMove tiles)
842                {
843                   int x,y;
844                   int c;
845                   scrabble.scores.Clear();
846                   scrabble.scores.ClearFields();
847                   scrabble.language = list.language;
848
849                   for(c = 0; c<MaxPlayers; c++)
850                   {
851                      strcpy(scrabble.playerNames[c], list.players[c]);
852                      scrabble.scoreFields[c] = null;
853
854                      if(scrabble.playerNames[c][0])
855                         scrabble.scores.AddField(scrabble.scoreFields[c] = DataField { dataType = class(int), header = scrabble.playerNames[c], width = 40 });
856                   }
857
858                   scrabble.gameStarted = true;
859
860                   for(y = 0; y<15; y++)
861                      for(x = 0; x<15; x++)
862                         scrabble.lastBoard[y][x] = scrabble.blankValues[y][x] = scrabble.boardLetters[y][x] = empty;
863
864                   scrabble.numLetters = 0;
865                   scrabble.numThrowLetters = 0;
866                   scrabble.dragging = false;
867                   scrabble.dragLetter = -1;
868
869                   scrabble.AddLetters(tiles);
870                   scrabble.Update(null);
871                   scrabble.turn = -1;
872                   do
873                   {
874                      scrabble.turn++;
875                      if(scrabble.turn == MaxPlayers) scrabble.turn = 0;
876                   } while(!scrabble.playerNames[scrabble.turn][0]);
877                }
878
879                void MovePlayed(PlayedMove move)
880                {
881                   int c;
882                   for(c = 0; c<move.numTiles; c++)
883                   {
884                      int x = move.tiles[c].x;
885                      int y = move.tiles[c].y;
886                      scrabble.boardLetters[y][x] = move.tiles[c].letter;
887                      scrabble.blankValues[y][x] = move.tiles[c].blankValue;
888                   }
889                   scrabble.playerScores[move.player] += move.score;
890                   scrabble.scores.AddRow().SetData(scrabble.scoreFields[move.player], scrabble.playerScores[move.player]);
891
892                   scrabble.Update(null);
893                   memcpy(scrabble.lastBoard, scrabble.boardLetters, sizeof(scrabble.lastBoard));
894
895                   do
896                   {
897                      scrabble.turn++;
898                      if(scrabble.turn == MaxPlayers) scrabble.turn = 0;
899                   } while(!scrabble.playerNames[scrabble.turn][0]);
900                }
901             };
902             incref server;
903             if(server.Connect(serverAddress.contents, 1494)) //3114))
904             {
905                playerID = server.Join();
906                if(playerID != -1)
907                   server.SetName(playerName.contents);
908             }
909             else
910                delete server;
911          }
912          return true;
913       }
914    };
915    Button startBtn
916    {
917       this, text = "Start", bevel = false, position = { 520, 222 };
918       font = { "Arial", 20, true };
919       opacity = 0; drawBehind = true;
920
921       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
922       {
923          if(scrabbleGame)
924             scrabbleGame.NewGame();
925          return true;
926       }
927    };
928    Button hostBtn
929    {
930       this, text = "Host (Français)", bevel = false, position = { 520, 322 };
931       font = { "Arial", 16, true };
932       opacity = 0; drawBehind = true;
933
934       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
935       {
936          if(!scrabbleGame || !scrabbleGame.gameStarted)
937          {
938             scrabbleService.Stop();
939             delete scrabbleGame;
940
941             scores.Clear();
942             scores.ClearFields();
943             scrabbleGame = ScrabbleGame { };
944             scrabbleGame.language = language = french;
945             scrabbleService.Start();
946             Update(null);
947          }
948          return true;
949       }
950    };
951    Button hostEnglishBtn
952    {
953       this, text = "Host (English)", bevel = false, position = { 520, 362 };
954       font = { "Arial", 16, true };
955       opacity = 0; drawBehind = true;
956
957       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
958       {
959          if(!scrabbleGame || !scrabbleGame.gameStarted)
960          {
961             scrabbleService.Stop();
962             delete scrabbleGame;
963
964             scores.Clear();
965             scores.ClearFields();
966             scrabbleGame = ScrabbleGame { };
967             scrabbleGame.language = language = english;
968             scrabbleService.Start();
969             Update(null);
970          }
971          return true;
972       }
973    };
974
975    EditBox playerName
976    {
977       this, text = "Name", contents = "[Your Name]", position = { 520, 10 }, size = { 100, 24 };
978    };
979    EditBox serverAddress
980    {
981       this, text = "Server", contents = "localhost", position = { 520, 40 }, size = { 100, 24 };
982    };
983    ListBox scores
984    {
985       master = this, hasMinimize = true, hasHeader = true, anchor = { right = 0 }, text = "Scores", size = { 300, 300 };
986    };
987
988    ~Scrabble()
989    {
990       // server.Disconnect(0);
991       delete server;
992    }
993 }
994 // TOCHECK: Doesn't work at the end
995 /*
996 Color valueColors[SquareValue] =
997 {
998    0, SkyBlue, SlateBlue, Pink, Red
999 };
1000 */
1001
1002 class ScrabbleApp : GuiApplication
1003 {
1004    bool Init()
1005    {
1006       SetLoggingMode(debug, 0);
1007       return true;
1008    }
1009
1010    void Terminate()
1011    {
1012       scrabbleService.Stop();
1013       delete scrabbleGame;
1014    }
1015 }
1016
1017 DCOMService scrabbleService { port = 1494 };