7 import remote "ScrabbleServer"
14 doubleLetter, dl = doubleLetter,
15 tripleLetter, tl = tripleLetter,
16 doubleWord, dw = doubleWord,
17 tripleWord, tw = tripleWord
25 define TrayHeight = 550;
27 define TrayWidth = 300;
29 define ThrowHeight = 620;
34 define MaxPlayers = 4;
36 SquareValue squareValues[15][15] =
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 }
54 Color valueColors[SquareValue] =
56 0, skyBlue, slateBlue, pink, red
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
69 int lettersCount[Languages][Letters] =
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
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
79 int lettersValue[Languages][Letters] =
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
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
91 class DefineBlank : Window
93 text = "Please choose the value of your blank tile";
99 position = { 20, 20 };
102 bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
104 if(list.currentRow) button.disabled = false;
108 bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
111 button.NotifyClicked(this, button, 0,0,0);
115 DataField field { class(Letters) };
122 position = { 130, 420 };
125 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
127 Destroy((DialogResult)(Letters)list.currentRow.GetData(field));
135 list.AddField(field);
136 for(c = 0; c<Letters::blank; c++)
138 list.AddRow().SetData(field, c);
143 class Scrabble : Window
145 Letters boardLetters[15][15];
146 Letters lastBoard[15][15];
147 Letters blankValues[15][15];
153 Letters throwLetters[7];
165 ServerConnection server;
170 char playerNames[MaxPlayers][256];
171 int playerScores[MaxPlayers];
176 text = "Ecere Scrabble";
177 borderStyle = sizable;
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 };
189 bool OnClose(bool parentClosing)
191 return !gameStarted || MessageBox { master = this, type = yesNo, text = "Ecere Scrabble", contents = "Are you sure you want to quit?" }.Modal() == yes;
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;
203 void DrawLetter(Surface surface, Letters a, int x, int y, bool blank)
207 surface.SetBackground(sandyBrown);
208 surface.Area(x+2, y+2, x + TileSize-1, y + TileSize-1);
210 if(a != Letters::blank)
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);
219 surface.TextFont(valueFont.font);
220 surface.WriteTextDotsf(right, x, y + TileSize - 14, TileSize+1, "%d", lettersValue[language][a]);
225 void DrawSquare(Surface surface, int cx, int cy)
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;
233 if(boardLetters[cy][cx] != empty)
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);
242 surface.SetBackground(color);
243 surface.SetForeground(color);
245 for(c = 0; c<num; c++)
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);
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);
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);
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);
277 if(boardLetters[cy][cx] == empty)
279 surface.Area(x + 2, y + 2, x + TileSize - 1, y + TileSize - 1);
280 surface.SetForeground(black);
281 if(cx == 7 && cy == 7)
285 int len = UTF32toUTF8Len(&star, 1, utf8, 4);
286 surface.TextFont(wingdings.font);
287 surface.WriteText(x - 1, y - 4, utf8, len);
291 surface.TextFont(scoreFont.font);
292 if(language == french)
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");
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");
307 else if(boardLetters[cy][cx] == empty)
309 surface.SetBackground(burlyWood);
310 surface.Area(x + 2, y + 2, x + TileSize - 1, y + TileSize - 1);
314 void OnRedraw(Surface surface)
318 surface.SetForeground(white);
319 for(y = 0; y<16; y++)
321 surface.HLine(BoardX, BoardX + TileSize * 15, BoardY + y * TileSize);
322 surface.HLine(BoardX, BoardX + TileSize * 15, BoardY + y * TileSize+1);
325 for(x = 0; x<16; x++)
327 surface.VLine(BoardY + 0, BoardY + TileSize * 15, BoardX + x * TileSize);
328 surface.VLine(BoardY + 0, BoardY + TileSize * 15, BoardX + x * TileSize+1);
330 for(y = 0; y<15; y++)
331 for(x = 0; x<15; x++)
333 DrawSquare(surface, x, y);
336 for(c = 0; c<numLetters; c++)
338 if(dragging && c == dragLetter)
339 DrawLetter(surface, letters[c], TrayLeft + moveX, TrayHeight - moveY, false);
341 DrawLetter(surface, letters[c], TrayLeft + positions[c], TrayHeight, false);
344 for(c = 0; c<numThrowLetters; c++)
346 DrawLetter(surface, throwLetters[c], TrayLeft + c * TileSize, ThrowHeight, false);
349 if(gameStarted && turn == playerID)
351 surface.SetForeground(red);
352 surface.TextFont(letterFont.font);
353 surface.WriteTextf(540, 400, "Your turn");
357 bool OnLeftButtonDown(int mx, int my, Modifiers mods)
361 // Picking Letters on the board
362 if(gameStarted && turn == playerID)
364 if(mx > BoardX && mx < BoardX + TileSize * 15 &&
365 my > BoardY && my < BoardY + TileSize * 15)
367 int x = (mx - BoardX) / TileSize;
368 int y = (my - BoardY) / TileSize;
369 if(boardLetters[y][x] != empty && lastBoard[y][x] == empty)
371 letters[numLetters] = boardLetters[y][x];
372 dragLetter = numLetters;
374 boardLetters[y][x] = empty;
375 blankValues[y][x] = empty;
384 moveX = x * TileSize + BoardX - TrayLeft;
385 moveY = TrayHeight - BoardY - y * TileSize;
395 // Picking letters in your hand
396 for(c = 0; c < numLetters; c++)
398 int x1 = TrayLeft + positions[c];
400 if(mx >= x1 && my >= y1 && mx < x1 + TileSize && my < y1 + TileSize)
407 moveX = positions[c];
408 startX = positions[c];
409 lastX = positions[c];
419 // Picking letters to throw
420 if(my > ThrowHeight && mx >= TrayLeft && mx < TrayLeft + numThrowLetters * TileSize)
422 int c = (mx - TrayLeft) / TileSize;
423 letters[numLetters] = throwLetters[c];
424 dragLetter = numLetters;
426 memmove(throwLetters + c, throwLetters + c + 1, sizeof(int) * (numThrowLetters - c - 1));
437 moveX = c * TileSize;
438 moveY = TrayHeight - ThrowHeight;
448 int ::ComparePositions(int * a, int * b)
450 int posA = (*a == scrabble.dragLetter) ? scrabble.moveX : scrabble.positions[*a];
451 int posB = (*b == scrabble.dragLetter) ? scrabble.moveX : scrabble.positions[*b];
461 bool OnMouseMove(int mx, int my, Modifiers mods)
469 bool collision = false;
471 y = startY + dragY - my;
473 x = startX + mx - dragX;
476 if(y < 0 && y > -TileSize && moveX >= TrayLeft && moveX < TrayLeft + TrayWidth) y = 0;
480 for(c = 0; c<numLetters; c++)
483 if(y < TileSize && y > -TileSize && moveX + TileSize >= 0 && moveX <= TrayWidth)
485 qsort(ordered, numLetters, sizeof(int), ComparePositions);
487 if(!out && dragPos != -1 && ordered[dragPos] != dragLetter)
490 int oldValue = ordered[dragPos];
491 for(c = 0; c<numLetters; c++)
492 if(ordered[c] == dragLetter)
499 memmove(ordered + pos, ordered + pos + 1, (dragPos - pos) * sizeof(int));
500 ordered[dragPos] = dragLetter;
504 memmove(ordered + dragPos + 1, ordered + dragPos, (pos - dragPos) * sizeof(int));
505 ordered[dragPos] = dragLetter;
508 ordered[dragPos] = dragLetter;
509 ordered[pos] = oldValue;
512 if(moveX + TileSize >= 0 && moveX <= TrayWidth)
514 for(c = 0; c<numLetters; c++)
518 if(positions[c] + TileSize > moveX &&
519 positions[c] < moveX + TileSize)
523 // COLLISION! (Insert in between?)
524 if(out && c != numLetters)
528 if(moveX >= lastX || collision)
532 for(c = 0; c<numLetters; c++)
534 if(ordered[c] == dragLetter)
541 end = moveX + TileSize;
543 for(; c < numLetters; c++)
545 if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) < end)
547 if(ordered[c] != dragLetter)
548 positions[ordered[c]] = end;
552 end = ((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize;
557 for(c = numLetters-1; c>= 0; c--)
559 if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize > end)
561 if(ordered[c] != dragLetter)
562 positions[ordered[c]] = end - TileSize;
564 moveX = end - TileSize;
566 end = (ordered[c] == dragLetter) ? moveX : positions[ordered[c]];
569 if(moveX < lastX || collision)
572 for(c = numLetters-1; c>=0; c--)
574 if(ordered[c] == dragLetter)
584 if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize > end)
586 if(ordered[c] != dragLetter)
587 positions[ordered[c]] = end - TileSize;
589 moveX = end - TileSize;
591 end = (ordered[c] == dragLetter) ? moveX : positions[ordered[c]];
596 for(c = 0; c<numLetters; c++)
598 if(((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) < end)
600 if(dragLetter != ordered[c])
601 positions[ordered[c]] = end;
605 end = ((ordered[c] == dragLetter) ? moveX : positions[ordered[c]]) + TileSize;
610 qsort(ordered, numLetters, sizeof(int), ComparePositions);
611 out = moveY >= TileSize || moveY < -TileSize;
614 for(c = 0; c<numLetters; c++)
615 if(ordered[c] == dragLetter)
621 // Dropping On the board
622 if(gameStarted && turn == playerID)
624 if(mx > BoardX && mx < BoardX + TileSize * 15 &&
625 my > BoardY && my < BoardY + TileSize * 15)
627 x = (mx - BoardX) / TileSize;
628 y = (my - BoardY) / TileSize;
629 if(boardLetters[y][x] == empty)
631 moveX = x * TileSize + BoardX - TrayLeft;
632 moveY = TrayHeight - BoardY - y * TileSize;
647 for(c = 0; c<numLetters; c++)
649 qsort(ordered, numLetters, sizeof(int), ComparePositions);
653 for(c = 0; c < numLetters; c++)
655 if(positions[ordered[c]] < end)
656 positions[ordered[c]] = end;
657 end = positions[ordered[c]] + TileSize;
662 for(c = numLetters-1; c>= 0; c--)
664 if(positions[ordered[c]] + TileSize > end)
665 positions[ordered[c]] = end - TileSize;
666 end = positions[ordered[c]];
670 bool OnLeftButtonUp(int mx, int my, Modifiers mods)
679 throwLetters[numThrowLetters++] = letters[dragLetter];
681 memmove(letters + dragLetter, letters + dragLetter + 1, sizeof(int) * (numLetters - dragLetter - 1));
682 memmove(positions + dragLetter, positions + dragLetter + 1, sizeof(int) * (numLetters - dragLetter - 1));
687 // Dropping Letters on the board
688 if(gameStarted && turn == playerID)
690 if(mx > BoardX && mx < BoardX + TileSize * 15 &&
691 my > BoardY && my < BoardY + TileSize * 15)
693 int x = (mx - BoardX) / TileSize;
694 int y = (my - BoardY) / TileSize;
695 if(boardLetters[y][x] == empty)
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));
702 if(boardLetters[y][x] == blank)
704 DefineBlank dialog { master = this };
705 blankValues[y][x] = (Letters)dialog.Modal();
713 positions[dragLetter] = moveX;
723 DataField scoreFields[MaxPlayers];
727 this, text = "Play", bevel = false, position = { 520, 540 };
728 font = { "Arial", 20, true };
729 opacity = 0; drawBehind = true;
731 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
733 if(!numThrowLetters && gameStarted && turn == playerID)
737 for(y = 0; y<15; y++)
739 for(x = 0; x<15; x++)
741 if(lastBoard[y][x] == empty && boardLetters[y][x] != empty)
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];
751 if(move.numTiles <= 7 && move.numTiles > 0)
753 if(server.PlayTiles(move))
755 memcpy(lastBoard, boardLetters, sizeof(lastBoard));
765 void AddLetters(PlayedMove move)
770 for(c = 0; c<numLetters; c++)
771 if(positions[c] + TileSize > end)
772 end = positions[c] + TileSize;
774 for(c = 0; c<move.numTiles; c++)
776 letters[numLetters] = move.tiles[c].letter;
777 positions[numLetters] = end;
786 this, text = "Throw / Pass", bevel = false, position = { 430, 622 };
787 font = { "Arial", 20, true };
788 opacity = 0; drawBehind = true;
790 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
793 for(y = 0; y<15; y++)
794 for(x = 0; x<15; x++)
795 if(boardLetters[y][x] != lastBoard[y][x])
798 if(!placed && gameStarted && turn == playerID)
802 for(c = 0; c<numThrowLetters; c++)
804 move.tiles[move.numTiles].letter = throwLetters[c];
808 if(server.DiscardTiles(move))
820 this, text = "Connect", bevel = false, position = { 520, 122 };
821 font = { "Arial", 20, true };
822 opacity = 0; drawBehind = true;
824 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
828 server = ServerConnection
830 void OnDisconnect(int code)
834 delete scrabble.server;
835 scrabble.gameStarted = false;
836 scrabble.Update(null);
838 DCOMClientObject::OnDisconnect(code);
841 void GameStarted(PlayerList list, PlayedMove tiles)
845 scrabble.scores.Clear();
846 scrabble.scores.ClearFields();
847 scrabble.language = list.language;
849 for(c = 0; c<MaxPlayers; c++)
851 strcpy(scrabble.playerNames[c], list.players[c]);
852 scrabble.scoreFields[c] = null;
854 if(scrabble.playerNames[c][0])
855 scrabble.scores.AddField(scrabble.scoreFields[c] = DataField { dataType = class(int), header = scrabble.playerNames[c], width = 40 });
858 scrabble.gameStarted = true;
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;
864 scrabble.numLetters = 0;
865 scrabble.numThrowLetters = 0;
866 scrabble.dragging = false;
867 scrabble.dragLetter = -1;
869 scrabble.AddLetters(tiles);
870 scrabble.Update(null);
875 if(scrabble.turn == MaxPlayers) scrabble.turn = 0;
876 } while(!scrabble.playerNames[scrabble.turn][0]);
879 void MovePlayed(PlayedMove move)
882 for(c = 0; c<move.numTiles; c++)
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;
889 scrabble.playerScores[move.player] += move.score;
890 scrabble.scores.AddRow().SetData(scrabble.scoreFields[move.player], scrabble.playerScores[move.player]);
892 scrabble.Update(null);
893 memcpy(scrabble.lastBoard, scrabble.boardLetters, sizeof(scrabble.lastBoard));
898 if(scrabble.turn == MaxPlayers) scrabble.turn = 0;
899 } while(!scrabble.playerNames[scrabble.turn][0]);
903 if(server.Connect(serverAddress.contents, 1494)) //3114))
905 playerID = server.Join();
907 server.SetName(playerName.contents);
917 this, text = "Start", bevel = false, position = { 520, 222 };
918 font = { "Arial", 20, true };
919 opacity = 0; drawBehind = true;
921 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
924 scrabbleGame.NewGame();
930 this, text = "Host (Français)", bevel = false, position = { 520, 322 };
931 font = { "Arial", 16, true };
932 opacity = 0; drawBehind = true;
934 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
936 if(!scrabbleGame || !scrabbleGame.gameStarted)
938 scrabbleService.Stop();
942 scores.ClearFields();
943 scrabbleGame = ScrabbleGame { };
944 scrabbleGame.language = language = french;
945 scrabbleService.Start();
951 Button hostEnglishBtn
953 this, text = "Host (English)", bevel = false, position = { 520, 362 };
954 font = { "Arial", 16, true };
955 opacity = 0; drawBehind = true;
957 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
959 if(!scrabbleGame || !scrabbleGame.gameStarted)
961 scrabbleService.Stop();
965 scores.ClearFields();
966 scrabbleGame = ScrabbleGame { };
967 scrabbleGame.language = language = english;
968 scrabbleService.Start();
977 this, text = "Name", contents = "[Your Name]", position = { 520, 10 }, size = { 100, 24 };
979 EditBox serverAddress
981 this, text = "Server", contents = "localhost", position = { 520, 40 }, size = { 100, 24 };
985 master = this, hasMinimize = true, hasHeader = true, anchor = { right = 0 }, text = "Scores", size = { 300, 300 };
990 // server.Disconnect(0);
994 // TOCHECK: Doesn't work at the end
996 Color valueColors[SquareValue] =
998 0, SkyBlue, SlateBlue, Pink, Red
1002 class ScrabbleApp : GuiApplication
1006 SetLoggingMode(debug, 0);
1012 scrabbleService.Stop();
1013 delete scrabbleGame;
1017 DCOMService scrabbleService { port = 1494 };