home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / comm / tcp / SlateGames.lha / reversi.rexx < prev   
OS/2 REXX Batch file  |  1995-12-19  |  17KB  |  565 lines

  1. /* Reversi for AmiSlate v1.0! */
  2. /* This program should be run on a screen with at least 8 colors */
  3.  
  4. /* Get our host's name--always given as first argument when run from Amislate */
  5. parse arg CommandPort ActiveString
  6.  
  7. if (length(CommandPort) == 0) then do
  8.     say ""
  9.     say "Usage:  rx reversi.rexx <REXXPORTNAME>"
  10.     say "        (REXXPORTNAME is usually AMISLATE)"
  11.     say ""
  12.     say "Or run from the Rexx menu within AmiSlate."
  13.     say ""
  14.     exit 0
  15.     end
  16.     
  17. /* Send all commands to this host */
  18. address (CommandPort) 
  19. options results
  20.  
  21. lock ON
  22.  
  23. /* See if we're connected */
  24. GetRemoteStateAttrs stem rstateattrs.
  25. if (rstateattrs.mode > -1) then do
  26.         /* Parse command line argument to see if we've been activated by 
  27.            a remote request or a local user */
  28.         check = upper(left(ActiveString,3))
  29.         if (upper(left(ActiveString,3)) ~= 'RE') then 
  30.             GlobData.localplayer = 1
  31.         else
  32.             GlobData.localplayer = -1
  33.         end
  34.     else do
  35.         GlobData.localplayer = 0    /* i.e. we're both players */
  36.         end
  37.         
  38. if (GlobData.localplayer > 0) then do
  39.     call SetStatus("Requesting game from remote user... please wait.")
  40.     RemoteRexxCommand '"'||"Would you like to play Reversi?"||'"' "slaterexx:reversi.rexx"
  41.     
  42.         waitevent stem handshake. MESSAGE
  43.         if (handshake.message == 0) then 
  44.         do
  45.             call SetStatus("Reversi Game Refused")
  46.             lock off
  47.             exit 0
  48.         end
  49.     end
  50.  
  51. call SetStatus("Beginning Reversi...")
  52.  
  53. call ResetGameState
  54. call SetGlobalData
  55. if (GlobData.localplayer >= 0) then call DrawBoard
  56.  
  57. call NextTurn
  58. call HandleEvents
  59.  
  60. lock OFF
  61. exit 0
  62.  
  63. /* Global Data structure:
  64.  
  65.     GlobData.Xspace.[step] (int)     : horizontal pixel offsets to the center of each coord (0-16)
  66.     GlobData.XSize    (int)         : total width of each element
  67.     GlobData.Yspace.[step] (int)     : vertical pixel offsets to the center of each coord (0-16)
  68.     GlobData.YSize    (int)         : total height of each element
  69.  
  70.     (board state)
  71.     GlobData.board.[0..8].[0..8]     : two-dimensional board array
  72.     
  73.     (game state)
  74.     GlobData.turn             : whose turn it is
  75.     GlobData.localplayer              : which player is on local machine; 0 if both are
  76.  
  77.      (color info)
  78.     GlobData.PieceColor.[playernum]  : color of pieces for this player
  79.  
  80. */
  81.  
  82. /* --------------------------------------------------------------- */
  83. /* procedure HandleEvents                        */
  84. /* --------------------------------------------------------------- */
  85. HandleEvents: procedure expose GlobData. AMessage.
  86.  
  87. AMessage.TIMEOUT     = 1    /* No events occurred in specified time period */
  88. AMessage.MESSAGE     = 2    /* Message recieved from remote Amiga */
  89. AMessage.MOUSEDOWN   = 4    /* Left mouse button press in drawing area */
  90. AMessage.MOUSEUP     = 8    /* Left mouse button release in drawing area */
  91. AMessage.RESIZE      = 16    /* Window was resized--time to redraw screen? */ 
  92. AMessage.QUIT        = 32    /* AmiSlate is shutting down */
  93. AMessage.CONNECT     = 64    /* Connection established */
  94. AMessage.DISCONNECT  = 128    /* Connection broken */
  95. AMessage.TOOLSELECT  = 256    /* Tool Selected */
  96. AMessage.COLORSELECT = 512    /* Palette Color selected */
  97. AMessage.KEYPRESS    = 1024    /* Key pressed */
  98. AMessage.MOUSEMOVE   = 2048     /* Mouse was moved */
  99.  
  100. do while(1)
  101.     waitevent stem event. RESIZE MOUSEDOWN MOUSEUP TOOLSELECT MESSAGE DISCONNECT
  102.  
  103.     if (event.type == AMessage.QUIT) then exit 0
  104.     
  105.     if (event.type == AMessage.DISCONNECT) then do
  106.         call SetStatus("Connection broken--both players now local.")
  107.         GlobData.localplayer = 0
  108.         end
  109.    
  110.     if (event.type == AMessage.MESSAGE) then do
  111.         parse var event.message rx ry
  112.         call PlaceMove((rx+0), (ry+0), 0)  /* update our internals--the (+0) forces the vars back into numeric format */
  113.         call NextTurn
  114.         end
  115.  
  116.     if (event.type == AMessage.RESIZE) then do
  117.         if ((GlobData.localplayer == GlobData.turn)|(GlobData.localplayer == 0)) then do
  118.             cturn = GlobData.turn
  119.             call SetGlobalData
  120.             call DrawBoard
  121.             call ColorBorder(GlobData.PieceColor.cturn)
  122.             call SetStatus("_LAST")
  123.         end
  124.         else do
  125.             /* else just update our internal vars */
  126.             call SetGlobalData
  127.             call SetStatus("_LAST")
  128.         end
  129.     end
  130.     
  131.     if ((event.type == AMessage.MOUSEDOWN)&((GlobData.turn = GlobData.localplayer)|(GlobData.localplayer == 0))) then do
  132.         whatclickedx = ChopRange(trunc((event.x-GlobData.BORDER_H)/(GlobData.XSize)),0,7)
  133.         whatclickedy = ChopRange(trunc((event.y-GlobData.BORDER_V)/(GlobData.YSize)),0,7)       
  134.         if (MoveOkay(whatclickedx, whatclickedy) == 1) then do
  135.             call PlaceMove(whatclickedx, whatclickedy, 1)
  136.             if (localplayer ~= 0) then call TransmitMove(whatclickedx, whatclickedy)
  137.             call NextTurn
  138.             end
  139.     end
  140.     end
  141.     return 1
  142.  
  143.  
  144. /* --------------------------------------------------------------- */
  145. /* procedure NextTurn                           */
  146. /*                                    */
  147. /* --------------------------------------------------------------- */
  148. NextTurn: procedure expose GlobData.
  149.     /* Swap turns */
  150.     GlobData.turn = -GlobData.turn
  151.  
  152.     negone = -1
  153.     player.1 = "Player 1"
  154.     player.negone = "Player 2"
  155.     cturn = GlobData.turn
  156.         
  157.     call SetStatus("Just a sec...")
  158.     if (GlobData.turn == GlobData.localplayer) then call ColorBorder(0)
  159.     
  160.     /* Check to see if the new player has a move available */
  161.     if (CanMove() == 0) then do
  162.         call SetStatus("Sorry, " || player.cturn || ", but you must forfeit your turn.")
  163.         call Delay(100)
  164.         call NextTurn    /* recursive! :) */
  165.         return 1
  166.         end
  167.  
  168.     /* local game code */
  169.     if (Globdata.localplayer == 0) then do
  170.         call SetStatus(player.cturn || ", it's your turn to place a stone.")
  171.         call ColorBorder(GlobData.PieceColor.cturn)
  172.         return 1
  173.         end
  174.     
  175.     /* two machine game code */
  176.     if (GlobData.turn == GlobData.localplayer) then do
  177.         call SetStatus(player.cturn || ", it's your turn to place a stone.")
  178.         call ColorBorder(GlobData.PieceColor.cturn)
  179.         end    
  180.     else call SetStatus("Wait for other player to move.")    
  181.     return 1
  182.  
  183.  
  184. /* --------------------------------------------------------------- */
  185. /* procedure ColorBorder                       */
  186. /*                                    */
  187. /* Colors the border the color indicated in the param            */
  188. /*                                   */
  189. /* --------------------------------------------------------------- */
  190. ColorBorder: procedure expose GlobData.
  191.     parse arg color
  192.     
  193.     SetFPen color
  194.     
  195.     /* Fill each side of the border with a square */
  196.     /* rely on AmiSlate's clipping to deal with extra material */
  197.     square 0 0 10000 (GlobData.YSpace.0)-1 FILL
  198.     square 0 0 (GlobData.XSpace.0)-1 10000 FILL
  199.     square (GlobData.XSpace.8)+1 0 10000 10000 FILL
  200.     square 0 (GlobData.YSpace.8)+1 10000 10000 FILL
  201.     return 1
  202.     
  203.  
  204. /* --------------------------------------------------------------- */
  205. /* procedure CanMove                           */
  206. /*                                    */
  207. /* Returns 1 if there is a move available for the current player   */
  208. /* else 0                               */
  209. /*                                    */
  210. /* --------------------------------------------------------------- */
  211. CanMove: procedure expose GlobData.
  212.     
  213.     num1 = 0
  214.     num2 = 0
  215.     negone = -1
  216.     
  217.     do i = 0 to 7
  218.         do j = 0 to 7
  219.              if (GlobData.board.i.j == 1) then 
  220.                  num1 = num1 + 1
  221.              else 
  222.                  if (GlobData.board.i.j == -1) then num2 = num2 + 1
  223.  
  224.             if (MoveOkay(i,j) == 1) then return 1
  225.         end
  226.     end
  227.     
  228.     /* If we got here there must not be an available move. */
  229.     /* If it's because the board is filled, then the game is over! */
  230.     if ((num1 + num2) == 64) then do
  231.         if (num1 > num2) then do
  232.             call ColorBorder(GlobData.PieceColor.1)
  233.             call GameWon("Player 1")
  234.             end
  235.         else if (num1 < num2) then do
  236.             call ColorBorder(GlobData.PieceColor.negone)
  237.             call GameWon("Player 2")
  238.             end
  239.         else do
  240.             call ColorBorder(0)
  241.             call GameWon("Tie")
  242.             end
  243.         end
  244.  
  245.     /* If it's because one of the players has no pieces left, then 
  246.        the game is also over! */
  247.     if (num1 == 0) then do
  248.         call ColorBorder(GlobData.PieceColor.negone)
  249.         call GameWon("Player 2")
  250.         end
  251.     if (num2 == 0) then do
  252.         call ColorBorder(GlobData.PieceColor.1)
  253.         call GameWon("Player 1")
  254.         end
  255.         
  256.     /* otherwise there just wasn't a move available */
  257.     return 0
  258.  
  259.  
  260. /* --------------------------------------------------------------- */
  261. /* procedure GameWon                           */
  262. /*                                    */
  263. /* displays a message saying who won, based on the arg, and exits. */
  264. /*                                   */
  265. /* --------------------------------------------------------------- */
  266. GameWon: procedure
  267.     parse arg winner
  268.     
  269.     if (winner == "Tie") then winstring = "The game ends in a tie."
  270.     else winstring = winner || " has won the game!"
  271.     
  272.     EasyRequest "Winner!" '"'||winstring||'"' "Okay"
  273.     call SetStatus("Game over.  Rerun script to play again.")
  274.     lock off
  275.     exit 0
  276.     
  277.  
  278. /* --------------------------------------------------------------- */
  279. /* procedure PlaceMove                           */
  280. /*                                    */
  281. /* does a move by placing a piece at (x,y) and turning over all    */
  282. /* appropriate pieces                           */
  283. /*                                   */
  284. /* --------------------------------------------------------------- */
  285. PlaceMove: procedure expose GlobData.
  286.     parse arg x,y, draw
  287.     
  288.     GlobData.board.x.y = GlobData.turn
  289.     if (draw == 1) then call DrawPiece(x,y, draw)
  290.     if (BracketAvailable(x,y,-1,-1)) then call DrawBracket(x,y,-1,-1, draw)
  291.     if (BracketAvailable(x,y,0,-1))  then call DrawBracket(x,y,0,-1, draw)
  292.     if (BracketAvailable(x,y,1,-1))  then call DrawBracket(x,y,1,-1, draw)
  293.     if (BracketAvailable(x,y,1,0))   then call DrawBracket(x,y,1,0, draw)
  294.     if (BracketAvailable(x,y,1,1))   then call DrawBracket(x,y,1,1, draw)
  295.     if (BracketAvailable(x,y,0,1))   then call DrawBracket(x,y,0,1, draw)
  296.     if (BracketAvailable(x,y,-1,1))  then call DrawBracket(x,y,-1,1, draw)
  297.     if (BracketAvailable(x,y,-1,0))  then call DrawBracket(x,y,-1,0, draw)
  298.     return 1
  299.  
  300.  
  301. /* --------------------------------------------------------------- */
  302. /* procedure MoveOkay                           */
  303. /*                                    */
  304. /* returns 1 if move is acceptable, or -1 if it is illegal.       */
  305. /*                                    */
  306. /* --------------------------------------------------------------- */
  307. MoveOkay: procedure expose GlobData.
  308.     parse arg x, y
  309.  
  310.     negone = -1
  311.     
  312.     /* first rule: you can only place a stone where there isn't one */
  313.     if (GlobData.board.x.y ~= 0) then return 0
  314.     
  315.     /* The only other rule: there must be a "bracket pair" in a direction */
  316.  
  317.     /* Try north first */
  318.     if (BracketAvailable(x,y,0,-1)  == 1) then return 1
  319.     if (BracketAvailable(x,y,0,1)   == 1) then return 1
  320.     if (BracketAvailable(x,y,-1,0)  == 1) then return 1
  321.     if (BracketAvailable(x,y,1,0)   == 1) then return 1
  322.     if (BracketAvailable(x,y,-1,-1) == 1) then return 1
  323.     if (BracketAvailable(x,y,1,-1)  == 1) then return 1
  324.     if (BracketAvailable(x,y,-1,1)  == 1) then return 1
  325.     if (BracketAvailable(x,y,1,1)   == 1) then return 1
  326.     
  327.     /* No move available */    
  328.     return 0
  329.     
  330.  
  331. /* --------------------------------------------------------------- */
  332. /* procedure BracketAvailable                       */
  333. /*                                   */
  334. /* Searches for an "us-them[...-them]-us" pattern in the direction */
  335. /* indicated by dx and dy.                        */
  336. /*                                   */
  337. /* --------------------------------------------------------------- */
  338. BracketAvailable: procedure expose GlobData.
  339.     parse arg x,y,dx,dy
  340.     
  341.     /* default */
  342.     BFoundMiddle = 0
  343.     
  344.     /* move off of the square-in-question... */
  345.     x = x + dx
  346.     y = y + dy
  347.     
  348.     do while ((x>=0)&(y>=0)&(x<=7)&(y<=7))
  349.         /* We hit a blank--no end on our bracket */
  350.         if (GlobData.board.x.y == 0) then return 0
  351.         
  352.         /* We hit our own piece--okay iff we saw theirs beforehand */
  353.         if (GlobData.board.x.y == GlobData.turn) then return BFoundMiddle
  354.  
  355.         /* We hit their piece--make a note of it */
  356.         if (GlobData.board.x.y == -GlobData.turn) then BFoundMiddle = 1
  357.  
  358.         /* advance to next square */
  359.         x = x + dx
  360.         y = y + dy
  361.         end
  362.     
  363.     /* Nope, ran off board */    
  364.     return 0
  365.     
  366.  
  367.  
  368. /* --------------------------------------------------------------- */
  369. /* procedure DrawBracket                       */
  370. /*                                   */
  371. /* Flips the counters for all of the other side's pieces in the    */
  372. /* given bracket.                            */
  373. /*                                   */
  374. /* --------------------------------------------------------------- */
  375. DrawBracket: procedure expose GlobData.
  376.     parse arg x,y,dx,dy,draw
  377.         
  378.     /* move off of the square-in-question... */
  379.     x = x + dx
  380.     y = y + dy
  381.  
  382.     do while ((x>=0)&(y>=0)&(x<=7)&(y<=7))    
  383.         /* We hit our own piece--we're all done */
  384.         if (GlobData.board.x.y == GlobData.turn) then return 1
  385.  
  386.         /* We hit their piece--flip it! */
  387.         if (GlobData.board.x.y == -GlobData.turn) then do
  388.             GlobData.board.x.y = GlobData.turn
  389.             if (draw == 1) then call DrawPiece(x,y,draw)
  390.             end
  391.  
  392.         /* advance to next square */
  393.         x = x + dx
  394.         y = y + dy
  395.         end
  396.  
  397.     /* Nope, ran off board */
  398.     return 0
  399.     
  400.  
  401. /* --------------------------------------------------------------- */
  402. /* procedure ResetGameState                       */
  403. /* --------------------------------------------------------------- */
  404. ResetGameState: procedure expose GlobData.
  405.     negone = -1;
  406.     
  407.     GlobData.turn = -1;
  408.     
  409.     /* first clear the board */
  410.     do i = 0 to 7
  411.         do j = 0 to 7
  412.             GlobData.board.i.j = 0
  413.         end
  414.     end
  415.  
  416.     /* then add in initial pieces */
  417.     GlobData.board.3.3 = 1
  418.     GlobData.board.4.4 = 1
  419.     GlobData.board.4.3 = -1
  420.     GlobData.board.3.4 = -1
  421.     return 1
  422.  
  423. /* --------------------------------------------------------------- */
  424. /* procedure SetGlobalData                       */
  425. /* --------------------------------------------------------------- */
  426. SetGlobalData: procedure expose GlobData.
  427.     negone = -1
  428.     
  429.     /* constants */
  430.     GlobData.BORDER_H = 5
  431.     GlobData.BORDER_V = 10
  432.     
  433.     /* Check to see whether we are connected */
  434.        GetWindowAttrs stem winattrs.
  435.        BoardWidth = winattrs.width  - 58 - (GlobData.BORDER_H*2)
  436.        BoardHeight= winattrs.height - 55 - (GlobData.BORDER_V*2)
  437.  
  438.     /* Set up offsets */
  439.     DO i=0 to 8
  440.       GlobData.Xspace.i = trunc(BoardWidth  * i / 8) + GlobData.BORDER_H
  441.       GlobData.Yspace.i = trunc(BoardHeight * i / 8) + GlobData.BORDER_V
  442.       end
  443.  
  444.     GlobData.XSize = trunc(BoardWidth / 8)
  445.     GlobData.YSize = trunc(BoardHeight / 8)
  446.  
  447.        if (winattrs.depth < 2) then do
  448.         EasyRequest Reversi_Error '"'||"You need at least a 4-color screen to play Reversi!"||'"' '"'||"Abort Reversi"||'"'
  449.         call SetStatus("Reversi game exited.")
  450.         lock off
  451.         exit 0
  452.         end 
  453.  
  454.     GlobData.PieceColor.1 = 2
  455.     GlobData.PieceColor.negone = 3
  456.     return 1
  457.  
  458.  
  459. /* --------------------------------------------------------------- */
  460. /* procedure DrawBoard                                             */
  461. /* --------------------------------------------------------------- */
  462. DrawBoard: procedure expose GlobData.
  463.     SetFColor 0 0 0        /* Get a black pen */
  464.  
  465.     Clear
  466.  
  467.     SetWindowTitle '"' || "Hang on, drawing the board..." || '"'
  468.  
  469.     SetFPen 1
  470.     do i = 0 to 8
  471.         line GlobData.XSpace.i GlobData.YSpace.0 GlobData.XSpace.i GlobData.YSpace.8
  472.         line GlobData.XSpace.0 GlobData.YSpace.i GlobData.XSpace.8 GlobData.YSpace.i
  473.         end
  474.         
  475.     do i = 0 to 7
  476.         do j = 0 to 7
  477.             if (GlobData.Board.i.j ~= 0) then call DrawPiece(i,j)
  478.             end
  479.         end
  480.         
  481.     call SetStatus("_LAST")
  482.     return 1
  483.  
  484.  
  485. /* Draws the piece listed at coords xc, yc */
  486. DrawPiece: procedure expose GlobData.
  487.     parse arg xc, yc
  488.  
  489.     piece = GlobData.board.xc.yc
  490.     if (piece == 0) then return 0
  491.     
  492.     /* Draw the piece in the square on the LOWER RIGHT of this intersection */
  493.     cx = MidwayBetween(xc,xc+1,X)
  494.     cy = MidwayBetween(yc,yc+1,Y)    
  495.     
  496.     SetFPen GlobData.PieceColor.piece
  497.     circle cx cy trunc(GlobData.XSize/3) trunc(GlobData.YSize/3) FILL
  498.     SetFPen 1
  499.     circle cx cy trunc(GlobData.XSize/3) trunc(GlobData.YSize/3) 
  500.     return 1
  501.  
  502.  
  503.  
  504. SetStatus: procedure
  505.     parse arg newstatus
  506.     
  507.     if (newstatus == "_LAST") then do
  508.         SetWindowTitle '"' || getclip("PrevString") || '"'
  509.         end
  510.     else do
  511.         call setclip("PrevString",newstatus)
  512.         SetWindowTitle '"' || newstatus || '"'
  513.     end
  514.     return 1
  515.     
  516.  
  517.     
  518.     
  519.     
  520.  
  521. /* Returns the point midway between two coords */
  522. MidWayBetween: procedure expose GlobData.
  523.     parse arg left, right, XorY
  524.  
  525.     if (XorY = X) then 
  526.         return trunc((GlobData.XSpace.left + GlobData.XSpace.right)/2)
  527.     else    
  528.         return trunc((GlobData.YSpace.left + GlobData.YSpace.right)/2)
  529.  
  530. ChopRange: procedure
  531.     parse arg myval, lo, hi
  532.     if (myval < lo) then return lo
  533.     if (myval > hi) then return hi
  534.     return myval
  535.     
  536.     
  537. /* --------------------------------------------------------------- */
  538. /* procedure CheckForWin                                           */
  539. /* --------------------------------------------------------------- */
  540. CheckForWin: procedure expose GlobData.
  541.     winner = nobody
  542.     negone = -1
  543.     
  544.     if (GlobData.exited.1 == 15) then winner = "Player 1"
  545.     else if (GlobData.exited.negone == 15) then winner = "Player 2"
  546.     
  547.     /* nobody one yet */
  548.     if (winner == nobody) then return 1
  549.     
  550.     EasyRequest "Winner!" '"'||winner||" has won the game!"||'"' "Okay"
  551.     call SetStatus("Game Over.  Rerun the script to play again.")
  552.     lock off
  553.     exit
  554.     return 0
  555.     
  556.         
  557. /* Transmit our move to our opponent */
  558. TransmitMove: procedure 
  559.     parse arg x, y
  560.     
  561.     sstring = '"' || x || " " || y || '"'
  562.     sendmessage sstring
  563.     return 1
  564.     
  565.