home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / tictactoe.C < prev    next >
C/C++ Source or Header  |  1998-12-02  |  12KB  |  563 lines

  1. // $Id: tictactoe.C,v 1.7 1998/12/02 10:23:47 zeller Exp $ -*- C++ -*-
  2. // Tic-Tac-Toe Game
  3.  
  4. // Copyright (C) 1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. // Based on a Java program by Scott L. Patterson <scott@uniqsys.com>.
  30. // See http://www.uniqsys.com/~scpatt/java/TicTacToe/ for details.
  31.  
  32. char tictactoe_rcsid[] = 
  33.     "$Id: tictactoe.C,v 1.7 1998/12/02 10:23:47 zeller Exp $";
  34.  
  35. #ifdef __GNUG__
  36. #pragma implementation
  37. #endif
  38.  
  39. #include "tictactoe.h"
  40.  
  41. #include "icons/tictactoe/circle.xbm"
  42. #include "icons/tictactoe/cross.xbm"
  43. #include "icons/tictactoe/empty.xbm"
  44.  
  45. #include <Xm/Xm.h>
  46. #include <Xm/RowColumn.h>
  47. #include <Xm/PushB.h>
  48. #include <Xm/SelectioB.h>    // XmCreatePromptDialog()
  49.  
  50. #include <iostream.h>
  51. #include <unistd.h>
  52.  
  53. #include "Command.h"
  54. #include "Delay.h"
  55. #include "DestroyCB.h"
  56. #include "HelpCB.h"
  57. #include "InitImage.h"
  58. #include "LessTifH.h"
  59. #include "TimeOut.h"
  60. #include "assert.h"
  61. #include "bool.h"
  62. #include "cook.h"
  63. #include "verify.h"
  64. #include "wm.h"
  65. #include "status.h"
  66. #include "version.h"
  67.  
  68.  
  69. // Board is: 1 2 3
  70. //           4 5 6
  71. //           7 8 9
  72.  
  73. static int board[10];
  74. static bool winning[10];
  75. static Widget buttons[10];
  76.  
  77. #define NO_ONE 0
  78. #define PLAYER1 1
  79. #define PLAYER2 4
  80. #define CATSEYE -1
  81.  
  82. // Search the rows for a near win
  83. static bool moveRow(int current_player)
  84. {
  85.     for (int x = 0; x < 3; x++)
  86.     {
  87.     int sum = 0;
  88.     for (int y = 1; y < 4; y++)
  89.         sum += board[(x * 3) + y];
  90.  
  91.     // Almost a win -- block it
  92.     if (sum == (current_player * 2))
  93.         for (int y = 1; y < 4; y++) 
  94.         if (board[(x * 3) + y] == NO_ONE) 
  95.         {
  96.             board[(x * 3) + y] = PLAYER2;
  97.             return true;
  98.                 }
  99.     }
  100.     return false;
  101. }
  102.  
  103. // Search the columns for a near win
  104. static bool moveColumn(int current_player)
  105. {
  106.     for (int x = 1; x < 4; x++)
  107.     {
  108.     int sum = 0;
  109.     for (int y = 0; y < 3; y++)
  110.         sum += board[x+(y*3)];
  111.  
  112.     // Almost a win -- block it
  113.     if (sum == (current_player * 2))
  114.         for (int y = 0; y < 3; y++)
  115.         if (board[x + (y * 3)] == NO_ONE)
  116.         {
  117.             board[x + (y * 3)] = PLAYER2;
  118.             return true;
  119.         }
  120.     }
  121.     return false;
  122. }
  123.  
  124. // Search the diagonals for a near win
  125. static bool moveDiagonal(int current_player) 
  126. {
  127.     static const int p1[3] = {1, 5, 9};
  128.     static const int p2[3] = {3, 5, 7};
  129.  
  130.     int sum = 0;
  131.  
  132.     int i;
  133.     for (i = 0; i < 3; i++)
  134.     sum += board[p1[i]];
  135.  
  136.     if (sum == (current_player*2))
  137.     for (i = 0; i < 3; i++)
  138.         if (board[p1[i]] == NO_ONE)
  139.         {
  140.         board[p1[i]] = PLAYER2;
  141.         return true;
  142.         }
  143.  
  144.     sum = 0;
  145.     for (i = 0; i < 3; i++)
  146.     sum += board[p2[i]];
  147.  
  148.     if (sum == (current_player * 2))
  149.     for (i = 0; i < 3; i++)
  150.         if (board[p2[i]] == NO_ONE)
  151.         {
  152.         board[p2[i]] = PLAYER2;
  153.         return true;
  154.         }
  155.     return false;
  156. }
  157.  
  158. // Computer AI
  159. // 1. Search for possible wins for me
  160. // 2. Search for possible wins for opponent
  161. // 3. Perform most strategic move
  162. static void autoMove()
  163. {
  164.     // Can I win
  165.     if (moveRow(PLAYER2)) 
  166.     return;
  167.     if (moveColumn(PLAYER2)) 
  168.     return;
  169.     if (moveDiagonal(PLAYER2)) 
  170.     return;
  171.  
  172.     // Can he/she win
  173.     if (moveRow(PLAYER1)) 
  174.     return;
  175.     if (moveColumn(PLAYER1)) 
  176.     return;
  177.     if (moveDiagonal(PLAYER1)) 
  178.     return;
  179.  
  180.     // Most strategic move
  181.     if (board[5] == NO_ONE)
  182.     {
  183.     board[5] = PLAYER2;
  184.     return;
  185.     }
  186.  
  187.     // Very Special cases
  188.     if ((board[5] == PLAYER2) && (board[9] == NO_ONE) &&
  189.     (board[8] == PLAYER1) && (board[3] == PLAYER1)) 
  190.     {
  191.     board[9] = PLAYER2;
  192.     return;
  193.     }
  194.  
  195.     if ((board[5] == PLAYER2) && (board[9] == NO_ONE) &&
  196.     (board[7] == PLAYER1) && (board[6] == PLAYER1)) 
  197.     {
  198.     board[9] = PLAYER2;
  199.     return;
  200.     }
  201.  
  202.     if ((board[5] == PLAYER2) && (board[7] == NO_ONE) &&
  203.     (board[8] == PLAYER1) && (board[1] == PLAYER1))
  204.     {
  205.     board[7] = PLAYER2;
  206.     return;     
  207.     }
  208.  
  209.     // Special cases
  210.     int *r = 0;
  211.     if ((board[5] == PLAYER2) && 
  212.     (((board[1] == PLAYER1) && (board[9] == PLAYER1)) ||
  213.      ((board[3] == PLAYER1) && (board[7] == PLAYER1))))
  214.     {
  215.     static int rtmp[9] = {5, 2, 4, 6, 8, 1, 3, 7, 9};
  216.     r = rtmp;
  217.     }
  218.     else if ((board[5] == PLAYER2) &&
  219.          (board[2] == PLAYER1) && (board[4] == PLAYER1))
  220.     {
  221.     static int rtmp[9] = {5, 1, 4, 6, 8, 2, 3, 7, 9};
  222.     r = rtmp;
  223.     }
  224.     else if ((board[5] == PLAYER2) &&
  225.          (board[2] == PLAYER1) && (board[6] == PLAYER1))
  226.     {
  227.     static int rtmp[9] = {5, 3, 4, 6, 8, 1, 2, 7, 9};
  228.     r = rtmp;
  229.     } 
  230.     else if ((board[5] == PLAYER2) &&
  231.          (board[8] == PLAYER1) && (board[4] == PLAYER1))
  232.     {
  233.     static int rtmp[9] = {5, 7, 4, 6, 8, 1, 3, 2, 9};
  234.     r = rtmp;
  235.     }
  236.     else if ((board[5] == PLAYER2) &&
  237.          (board[8] == PLAYER1) && (board[6] == PLAYER1))
  238.     {
  239.     static int rtmp[9] = {5, 9, 4, 6, 8, 1, 3, 7, 2};
  240.     r = rtmp;
  241.     }
  242.     else 
  243.     {
  244.     static int tics = 0;
  245.     if (++tics % 10 == 0)
  246.     {
  247.         // In one out of 10 cases, give the user a chance.
  248.         static int rtmp[9] = {5, 2, 4, 6, 8, 1, 3, 7, 9};
  249.         r = rtmp;
  250.     }
  251.     else
  252.     {
  253.         static int rtmp[9] = {5, 1, 3, 7, 9, 2, 4, 6, 8};
  254.         r = rtmp;
  255.     }
  256.     }
  257.  
  258.     for (int i = 0; i < 9; i++)
  259.     if (board[r[i]] == NO_ONE) {
  260.         board[r[i]] = PLAYER2;
  261.         return;
  262.     }
  263. }
  264.  
  265. static int winner() 
  266. {
  267.     int i;
  268.     for (i = 1; i < 10; i++)
  269.     winning[i] = false;
  270.  
  271.     // Check for diagonal win
  272.     int sum;
  273.  
  274.     sum = board[3] + board[5] + board[7];
  275.     if (sum == 3 || sum == 12)
  276.     {
  277.     winning[3] = winning[5] = winning[7] = true;
  278.     return sum == 3 ? PLAYER1 : PLAYER2;
  279.     }
  280.  
  281.     sum = board[1] + board[5] + board[9];
  282.     if (sum == 3 || sum == 12)
  283.     {
  284.     winning[1] = winning[5] = winning[9] = true;
  285.     return sum == 3 ? PLAYER1 : PLAYER2;
  286.     }
  287.  
  288.     // Search the columns for a win
  289.     int x;
  290.     for (x = 1; x < 4; x++) 
  291.     {
  292.     sum = 0;
  293.     for (int y = 0; y < 3; y++)
  294.         sum += board[x + (y * 3)];
  295.  
  296.     if (sum == 3 || sum == 12)
  297.     {
  298.         for (int y = 0; y < 3; y++)
  299.         winning[x + (y * 3)] = true;
  300.         
  301.         return sum == 3 ? PLAYER1 : PLAYER2;
  302.     }
  303.     }
  304.  
  305.     // Search the rows for a near win
  306.     for (x = 0; x < 3; x++)
  307.     {
  308.     sum = 0;
  309.     for (int y = 1; y < 4; y++)
  310.         sum += board[(x * 3) + y];
  311.  
  312.     if (sum == 3 || sum == 12)
  313.     {
  314.         for (int y = 1; y < 4; y++)
  315.         winning[(x * 3) + y] = true;
  316.         
  317.         return sum == 3 ? PLAYER1 : PLAYER2;
  318.     }
  319.     }
  320.  
  321.     // Check for all squares filled
  322.     for (i = 1; i < 10; i++)
  323.     if (board[i] == NO_ONE)
  324.         return NO_ONE;
  325.  
  326.     return CATSEYE;
  327. }
  328.  
  329. static void initBoard() 
  330. {
  331.     for (int i = 1; i < 10; i++)
  332.     board[i] = NO_ONE;
  333. }
  334.  
  335. // Convert NAME into a color, using PIX as default
  336. static Pixel color(Widget w, String name, Pixel pixel)
  337. {
  338.     XrmValue from, to;
  339.     from.size = strlen(name);
  340.     from.addr = name;
  341.     to.size   = sizeof(pixel);
  342.     to.addr   = (String)&pixel;
  343.  
  344.     XtConvertAndStore(w, XtRString, &from, XtRPixel, &to);
  345.     return pixel;
  346. }
  347.  
  348. static void repaint()
  349. {
  350.     int win = winner();
  351.  
  352.     for (int i = 1; i <= 9; i++)
  353.     {
  354.     char *name = 0;
  355.     switch (board[i])
  356.     {
  357.     case NO_ONE:
  358.         name = "empty";
  359.         break;
  360.  
  361.     case PLAYER1:
  362.         name = "circle";
  363.         break;
  364.  
  365.     case PLAYER2:
  366.         name = "cross";
  367.         break;
  368.     }
  369.  
  370.     assert(name != 0);
  371.  
  372.     Pixel foreground;
  373.     Pixel background;
  374.     XtVaGetValues(buttons[i], 
  375.               XmNforeground, &foreground,
  376.               XmNbackground, &background, 
  377.               NULL);
  378.  
  379.     if (win == NO_ONE || winning[i])
  380.     {
  381.         if (board[i] == PLAYER1)
  382.         foreground = color(buttons[i], "red4",
  383.                    BlackPixelOfScreen(XtScreen(buttons[i])));
  384.         else
  385.         foreground = BlackPixelOfScreen(XtScreen(buttons[i]));
  386.     }
  387.  
  388.     Pixmap p = XmGetPixmap(XtScreen(buttons[i]), name, 
  389.                    foreground, background);
  390.     XtVaSetValues(buttons[i],
  391.               XmNlabelType, XmPIXMAP,
  392.               XmNlabelPixmap, p,
  393.               XmNlabelInsensitivePixmap, p,
  394.               NULL);
  395.  
  396.     XtSetSensitive(buttons[i], win == NO_ONE && board[i] == NO_ONE);
  397.     }
  398.  
  399.     switch (win)
  400.     {
  401.     case PLAYER1:
  402.     set_status("You win.");
  403.     break;
  404.  
  405.     case PLAYER2:
  406.     set_status(DDD_NAME " wins.");
  407.     break;
  408.  
  409.     case CATSEYE:
  410.     set_status("Cat's eye.");
  411.     break;
  412.     }
  413. }
  414.  
  415. static void MoveCB(XtPointer client_data, XtIntervalId *id)
  416. {
  417.     (void) id;            // Use it
  418.     XtIntervalId *timer = (XtIntervalId *)client_data;
  419.     assert(*timer == *id);
  420.     *timer = 0;
  421.  
  422.     autoMove();
  423.     repaint();
  424. }
  425.  
  426. static const int THINKING_TIME = 0; // `Thinking' time in ms
  427.  
  428. static void make_move(int move)
  429. {
  430.     static XtIntervalId timer = 0;
  431.  
  432.     // Is it a valid move?
  433.     if ((move < 1) || (move > 9) || (board[move] != NO_ONE))
  434.     return;
  435.  
  436.     // Are we still thinking?
  437.     if (timer != 0)
  438.     return;
  439.  
  440.     board[move] = PLAYER1;
  441.     repaint();
  442.  
  443.     if (winner() == NO_ONE)
  444.     {
  445.     for (int i = 1; i < 10; i++)
  446.         XtSetSensitive(buttons[i], False);
  447.  
  448.     // Make a move in THINKING_TIME ms
  449.     timer = XtAppAddTimeOut(XtWidgetToApplicationContext(buttons[move]),
  450.                 THINKING_TIME, MoveCB, XtPointer(&timer));
  451.     }
  452. }
  453.  
  454. // Install the given X bitmap as NAME
  455. static void InstallImage(unsigned char *bits, int width, int height, 
  456.              const string& name)
  457. {
  458.     XImage *image = CreateImageFromBitmapData(bits, width, height);
  459.     Boolean ok = XmInstallImage(image, name);
  460.     if (!ok)
  461.     cerr << "Could not install " << quote(name) << " bitmap\n";
  462. }
  463.  
  464. static void install_images()
  465. {
  466.     static bool installed = false;
  467.     if (installed)
  468.     return;
  469.  
  470.     InstallImage(cross_bits,  cross_width,  cross_height,  "cross");
  471.     InstallImage(circle_bits, circle_width, circle_height, "circle");
  472.     InstallImage(empty_bits,  empty_width,  empty_height,  "empty");
  473.  
  474.     installed = true;
  475. }
  476.  
  477. static void ResetTicTacToeCB(Widget, XtPointer, XtPointer)
  478. {
  479.     static int tics = 0;
  480.  
  481.     initBoard();
  482.  
  483.     if (tics++ % 2 == 0)
  484.     autoMove();
  485.  
  486.     repaint();
  487.  
  488.     set_status("Welcome to Tic Tac Toe!");
  489. }
  490.  
  491. static void MakeMoveCB(Widget, XtPointer client_data, XtPointer)
  492. {
  493.     if (winner() != NO_ONE)
  494.     initBoard();
  495.  
  496.     make_move((int)client_data);
  497. }
  498.  
  499. static Widget create_tictactoe(Widget parent)
  500. {
  501.     static Widget board = 0;
  502.     if (board != 0)
  503.     return board;
  504.  
  505.     install_images();
  506.  
  507.     Arg args[10];
  508.     int arg = 0;
  509.     XtSetArg(args[arg], XmNorientation, XmHORIZONTAL);  arg++;
  510.     XtSetArg(args[arg], XmNpacking,     XmPACK_COLUMN); arg++;
  511.     XtSetArg(args[arg], XmNnumColumns,  3);             arg++;
  512.     board = XmCreateRowColumn(parent, "board", args, arg);
  513.  
  514.     for (int i = 1; i <= 9; i++)
  515.     {
  516.     arg = 0;
  517.     buttons[i] = XmCreatePushButton(board, "field", args, arg);
  518.     XtManageChild(buttons[i]);
  519.     XtAddCallback(buttons[i], XmNactivateCallback, 
  520.               MakeMoveCB, XtPointer(i));
  521.     }
  522.     XtManageChild(board);
  523.  
  524.     return board;
  525. }
  526.  
  527. void TicTacToeCB(Widget, XtPointer, XtPointer)
  528. {
  529.     static Widget dialog = 0;
  530.     if (dialog == 0)
  531.     {
  532.     Arg args[10];
  533.     int arg = 0;
  534.  
  535.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  536.     dialog = verify(XmCreatePromptDialog(find_shell(),
  537.                          "tictactoe", args, arg));
  538.     Delay::register_shell(dialog);
  539.  
  540.     if (lesstif_version <= 79)
  541.         XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  542.                            XmDIALOG_APPLY_BUTTON));
  543.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  544.                            XmDIALOG_HELP_BUTTON));
  545.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  546.                            XmDIALOG_CANCEL_BUTTON));
  547.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  548.                            XmDIALOG_TEXT));
  549.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  550.                            XmDIALOG_SELECTION_LABEL));
  551.  
  552.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
  553.     XtAddCallback(dialog, XmNokCallback, ResetTicTacToeCB, NULL);
  554.     XtAddCallback(dialog, XmNcancelCallback, UnmanageThisCB, 
  555.               XtPointer(dialog));
  556.  
  557.     create_tictactoe(dialog);
  558.     }
  559.  
  560.     ResetTicTacToeCB(0, 0, 0);
  561.     manage_and_raise(dialog);
  562. }
  563.