Advertisement
Guest User

Untitled

a guest
Dec 11th, 2018
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 231.68 KB | None | 0 0
  1. package control;
  2.  
  3.  
  4. //*******************************************************************
  5. //
  6. // GAME OF CONTROL
  7. //
  8. // (c) Joachim Parrow 2003, 2006, 2010
  9. //
  10. //*******************************************************************
  11.  
  12. import java.awt.*;
  13. import javax.swing.*;
  14. import javax.swing.border.*;
  15. import java.awt.event.*;
  16. import java.net.*;
  17. import java.io.*;
  18. import java.awt.geom.*;
  19. import java.awt.image.*;
  20. import java.util.ArrayList;
  21. import java.util.Random;
  22. import java.util.concurrent.*;
  23.  
  24. public class Control
  25.  
  26. {
  27.  
  28. static final String VERSION_NUMBER = "6.3"; // version number
  29. static final boolean testSetup = false; // If true, start a test session
  30.  
  31. public static void main(String[] args)
  32. {
  33.  
  34. // The following hack is needed to make the background colors of buttons
  35. // show on a mac. If it makes trouble just delete it.
  36.  
  37. String laf = UIManager.getCrossPlatformLookAndFeelClassName();
  38. try {
  39. UIManager.setLookAndFeel(laf);
  40. } catch (Exception e) {
  41. System.err.println("Error loading L&F: " + e.getMessage());
  42. }
  43.  
  44.  
  45. // main just starts one instance of StartControl - or two instances if testing.
  46.  
  47. if (!testSetup) {
  48. new StartControl(false, false); // Normal start: Here goes!
  49. } else {
  50. new StartControl(true, false); // Test start: start a listener
  51. new StartControl(false, true); // and then start a client on the same thread (OK for test purposes!)
  52. }
  53. }
  54. }
  55.  
  56. //*************************************************************************
  57. //
  58. // Start Control
  59. //
  60. // Handles all user interaction to start a game of control
  61. //
  62. //*************************************************************************
  63.  
  64.  
  65. class StartControl implements ActionListener
  66. {
  67. // Graphic common variables
  68.  
  69. final private JFrame myFrame = new JFrame("The game of Control v" + Control.VERSION_NUMBER);
  70.  
  71. final private Container thePane = myFrame.getContentPane();
  72.  
  73. final private JButton connectButton = new JButton("Connect"); // The Buttons on the main screen
  74. final private JButton quitButton = new JButton("Quit");
  75. final private JButton listenButton = new JButton("Listen");
  76. final private JButton abortButton = new JButton("Abort");
  77. final private JButton playButton = new JButton("Play");
  78. final private JButton recordButton = new JButton("Record");
  79. final private JButton playbackButton = new JButton("Playback");
  80. final private JButton optionsButton = new JButton("Options");
  81. final private JButton defaultsButton = new JButton("Defaults");
  82. final private JButton returnButton = new JButton("Return");
  83.  
  84. private JTextField adrField; // input of opponent host name to connect to
  85. private JTextField nickField; // input of user nick
  86.  
  87. // Button explanatory labels
  88.  
  89. final private JLabel connectLabel = new JLabel(" Press Connect to connect to another computer", SwingConstants.RIGHT);
  90. final private JLabel quitLabel = new JLabel(" Press Quit to exit this program", SwingConstants.RIGHT);
  91. final private JLabel listenLabel = new JLabel(" Press Listen to listen for connections", SwingConstants.RIGHT);
  92. final private JLabel abortLabel = new JLabel(" Press Abort to return to main screen", SwingConstants.RIGHT);
  93. final private JLabel playLabel = new JLabel(" Press Play to start the game", SwingConstants.RIGHT);
  94. final private JLabel recordLabel = new JLabel(" Press Record to choose a file for recording the game", SwingConstants.RIGHT);
  95. final private JLabel playbackLabel = new JLabel(" Press Playback to playback a recorded game", SwingConstants.RIGHT);
  96. final private JLabel optionsLabel = new JLabel(" Press Options to change options", SwingConstants.RIGHT);
  97. final private JLabel returnLabel = new JLabel(" Set options and return to main menu", SwingConstants.RIGHT);
  98. final private JLabel defaultsLabel = new JLabel(" Reset to default values", SwingConstants.RIGHT);
  99. private JLabel ipLabel;
  100.  
  101.  
  102. final private JPanel connectPanel = new JPanel(); // panels containing buttons and their labels
  103. final private JPanel quitPanel = new JPanel();
  104. final private JPanel listenPanel = new JPanel();
  105. final private JPanel abortPanel = new JPanel();
  106. final private JPanel playPanel = new JPanel();
  107. final private JPanel recordPanel = new JPanel();
  108. final private JPanel playbackPanel = new JPanel();
  109. final private JPanel optionsPanel = new JPanel();
  110. final private JPanel returnPanel = new JPanel();
  111. final private JPanel defaultsPanel = new JPanel();
  112.  
  113. // The following is all concerned with the options screen
  114.  
  115. final private JRadioButton compactButton = new JRadioButton("Compact"); // graphic option radio buttons
  116. final private JRadioButton largeButton = new JRadioButton ("Large");
  117. final private JRadioButton hugeButton = new JRadioButton("Huge");
  118.  
  119. final private JRadioButton fastButton = new JRadioButton("Fast"); // speed option radio buttons
  120. final private JRadioButton normalButton = new JRadioButton ("Normal");
  121. final private JRadioButton slowButton = new JRadioButton("Slow");
  122.  
  123. final private JRadioButton squareButton = new JRadioButton("Square"); // Area shape radio buttons
  124. final private JRadioButton hexButton = new JRadioButton("Hexagonal");
  125. final private JRadioButton circleButton = new JRadioButton("Circle");
  126.  
  127. final private JRadioButton boardCircleButton = new JRadioButton("Round"); // board shape radio buttons
  128. final private JRadioButton boardSquareButton = new JRadioButton("Rectangular");
  129.  
  130. final private JRadioButton liteButton = new JRadioButton("Controlite (no heavy pieces)"); // Game type radio buttons
  131. final private JRadioButton controlButton = new JRadioButton("Control (all pieces)");
  132.  
  133. final private JRadioButton noHillsButton = new JRadioButton("Flat board"); // Board topography radio buttons
  134. final private JRadioButton hillsButton = new JRadioButton("Hills on the board");
  135.  
  136. final private int[] sizeChoices = {8,10,12,14,16}; // allowed choices of width and height and pebbles
  137.  
  138. final private String[] widthChoisesStr = {"Width: 8","Width: 10","Width: 12","Width: 14","Width: 16"};
  139. final private String[] heightChoisesStr = {"Height: 8","Height: 10","Height: 12","Height: 14","Height: 16"};
  140. final private String[] pebblesChoisesStr = {"Pebbles: 8","Pebbles: 10","Pebbles: 12","Pebbles: 14","Pebbles: 16"};
  141.  
  142.  
  143. final private int[] lengthChoices = {5,10,15,20,30}; // allowed choices of max game length
  144. final private String[] lengthChoicesStr = {" 5 min.","10 min.","15 min.","20 min.","30 min."};
  145.  
  146. final private JComboBox widthBox = new JComboBox(widthChoisesStr);
  147. final private JComboBox heightBox = new JComboBox(heightChoisesStr);
  148. final private JComboBox pebblesBox = new JComboBox(pebblesChoisesStr);
  149. final private JComboBox lengthBox = new JComboBox(lengthChoicesStr);
  150.  
  151.  
  152.  
  153. // ----------------- Texts to inform the user ----------------------------------------
  154.  
  155. final private String welcomeStatus = " Welcome to the game of Control!\n"+
  156. " (c) Joachim Parrow 2003,2006,2010\n\n"+
  157. "You are currently not connected to another Player. "+
  158. "To attempt to connect, press the Connect Button above ("+
  159. "you will need to tell me the IP of the computer to connect to). " +
  160. "Alternatively, to listen in case someone tries to connect to you " +
  161. "press the Listen Button.";
  162.  
  163. final private String listenStatus = "You are currently listening for someone to connect to you.\n\n"+
  164. "When this happens I shall notify you and ask if you want to accept the connection. "+
  165. "Until then there is nothing to do but wait. "+
  166. "Pressing Abort above will mean that you give up listening.";
  167.  
  168. final private String connectingStatus = "Please enter either the IP name or the number "+
  169. "you want to connect to in the field above (for example 'myhost.edu.com' or '137.0.1.15'. Pressing <return> "+
  170. "in this field will make me start connecting to it. "+
  171. "Pressing Abort returns to main screen.";
  172.  
  173. final private String acceptingstatus = "An opponent at the address given above is ready to play.\n\n"+
  174. "Accept the challange by pressing the Play button. "+
  175. "If you do not wish to play this opponent press Abort "+
  176. "which returns you to the main menu";
  177.  
  178. final private String connectedStatus = "You have successfully connected to a server at the address given above. "+
  179. "Now you must wait for a user at that address to accept to play with you. "+
  180. "When that happens the game will start. "+
  181. "Until then you must wait. If you get tired Abort takes you back to the main menu "+
  182. "and the connection will be lost.";
  183.  
  184. final private String optionsStatus = "Here you can set options for your game. A nick is optional and will only be used "+
  185. "to tell your opponent who you are."+
  186. "The other options must be set in the same way by both players.\n\n"+
  187. "Game type: Choose controlite (only Pebbles and Squares) or Control (all heavy pieces).\n\n"+
  188. "Hills: Hills are randomly distributed; a piece on a hill exerts control over a larger area. "+
  189. "Choose to play with hills on the board or not.\n\n"+
  190. "Board shape is the shape of the board, while area shape is the shape of the area a piece controls.\n\n"+
  191. "The relative board dimensions adjust the size of the board in relation to the size of pieces, while the choice of graphics "+
  192. "determines how large it appears on the screen.\n\n"+
  193. "Game speed determines how long a player has to wait between moves, and length is the duration of a game.\n\n"+
  194. "Finally, Pebbles is the number of Pebbles available to each player (the initial Pebble plus the additional Pebbles that may be built).";
  195.  
  196.  
  197.  
  198. // Communication variables
  199.  
  200. private Socket outSocket = null; // outgoing communications socket to other player
  201. private ServerSocket inSocket = null; // incoming communications server from other player
  202. private Socket connection = null; // incoming communications socket
  203. private ObjectOutputStream outgoing; // outgoing stream to opponent
  204. private ObjectInputStream incoming; // incoming stream from opponent
  205.  
  206. private GetConnection getConnection = null; // threads listening for connections
  207. private GetAccept getAccept = null;
  208.  
  209.  
  210. private ObjectOutputStream record; // for recording games
  211. private ObjectInputStream playback; // for recorded games
  212.  
  213. final private static int PORT_NUMBER = 8888; // port number that this game uses
  214.  
  215. // Game logic variables
  216.  
  217. private boolean playWhite; // set if I (randomly) got to play white
  218.  
  219. private String nick = ""; // your nickname
  220. private String opponentNick = ""; // opponent's nickname
  221. private String lastAddress = ""; // address typed in when connecting
  222.  
  223. private int graphics, shape, boardShape, speed, width, height, pebbles, length; // game params
  224. private boolean isLite, withHills;
  225.  
  226. private int widthIdx, heightIdx, pebblesIdx, lengthIdx; // idx to the options
  227.  
  228. // default options
  229.  
  230. final private static int DEFAULT_GRAPHICS = 1, DEFAULT_SHAPE = 3, DEFAULT_BOARDSHAPE = 1,
  231. DEFAULT_SPEED = 1, DEFAULT_WIDTH = 8, DEFAULT_HEIGHT = 8,
  232. DEFAULT_PEBBLES = 8, DEFAULT_LENGTH = 10;
  233. final private static boolean DEFAULT_ISLITE = false;
  234. final private static boolean DEFAULT_WITHHILLS = true;
  235.  
  236. // test parameters
  237.  
  238. private boolean testServer; // true if testing and I should set up a server
  239. private boolean testClient; // true if testing and I should set up a client
  240.  
  241. //-------------------------------------------------------------------------------
  242. // Constructor just initialises first frame
  243. //-------------------------------------------------------------------------------
  244.  
  245.  
  246.  
  247. StartControl(boolean testServer, boolean testClient)
  248. {
  249. quitButton.setBackground (Color.red); // set button colors need to redifne laf to work on a Mac :(
  250. connectButton.setBackground (Color.green);
  251. listenButton.setBackground (Color.green);
  252. playButton.setBackground (Color.green);
  253. abortButton.setBackground (Color.orange);
  254. recordButton.setBackground (Color.yellow);
  255. playbackButton.setBackground(Color.yellow);
  256. optionsButton.setBackground (Color.yellow);
  257. returnButton.setBackground (Color.green);
  258. defaultsButton.setBackground(Color.orange);
  259.  
  260. Color buttonColor = Board.BACKGROUNDCOLOR.brighter(); // color for buttons in options panel
  261.  
  262. controlButton.setBackground (buttonColor);
  263. liteButton.setBackground (buttonColor);
  264.  
  265. hillsButton.setBackground (buttonColor);
  266. noHillsButton.setBackground (buttonColor);
  267.  
  268. compactButton.setBackground (buttonColor);
  269. largeButton.setBackground (buttonColor);
  270. hugeButton.setBackground (buttonColor);
  271.  
  272. fastButton.setBackground (buttonColor);
  273. normalButton.setBackground (buttonColor);
  274. slowButton.setBackground (buttonColor);
  275.  
  276. squareButton.setBackground (buttonColor);
  277. hexButton.setBackground (buttonColor);
  278. circleButton.setBackground (buttonColor);
  279.  
  280. boardCircleButton.setBackground (buttonColor);
  281. boardSquareButton.setBackground (buttonColor);
  282.  
  283.  
  284. widthBox.setBackground (buttonColor);
  285. heightBox.setBackground (buttonColor);
  286. pebblesBox.setBackground (buttonColor);
  287. lengthBox.setBackground (buttonColor);
  288.  
  289. quitPanel.add(quitButton); // build button panels
  290. connectPanel.add(connectButton);
  291. listenPanel.add(listenButton);
  292. abortPanel.add(abortButton);
  293. playPanel.add(playButton);
  294. recordPanel.add(recordButton);
  295. playbackPanel.add(playbackButton);
  296. optionsPanel.add(optionsButton);
  297. returnPanel.add(returnButton);
  298. defaultsPanel.add(defaultsButton);
  299.  
  300. quitPanel.setBackground(Board.BACKGROUNDCOLOR); // set panel backgrounds
  301. listenPanel.setBackground(Board.BACKGROUNDCOLOR);
  302. connectPanel.setBackground(Board.BACKGROUNDCOLOR);
  303. abortPanel.setBackground(Board.BACKGROUNDCOLOR);
  304. playPanel.setBackground(Board.BACKGROUNDCOLOR);
  305. recordPanel.setBackground(Board.BACKGROUNDCOLOR);
  306. playbackPanel.setBackground(Board.BACKGROUNDCOLOR);
  307. optionsPanel.setBackground(Board.BACKGROUNDCOLOR);
  308. returnPanel.setBackground(Board.BACKGROUNDCOLOR);
  309. defaultsPanel.setBackground(Board.BACKGROUNDCOLOR);
  310.  
  311. quitButton.addActionListener(this); // add me as listener for all buttons
  312. connectButton.addActionListener(this);
  313. listenButton.addActionListener(this);
  314. abortButton.addActionListener(this);
  315. playButton.addActionListener(this);
  316. recordButton.addActionListener(this);
  317. playbackButton.addActionListener(this);
  318. optionsButton.addActionListener(this);
  319. returnButton.addActionListener(this);
  320. defaultsButton.addActionListener(this);
  321.  
  322. graphics = DEFAULT_GRAPHICS; // set params to defaults
  323. speed = DEFAULT_SPEED;
  324. length = DEFAULT_LENGTH;
  325. height = DEFAULT_HEIGHT;
  326. width = DEFAULT_WIDTH;
  327. pebbles = DEFAULT_PEBBLES;
  328. shape = DEFAULT_SHAPE;
  329. isLite = DEFAULT_ISLITE;
  330. boardShape = DEFAULT_BOARDSHAPE;
  331. withHills = DEFAULT_WITHHILLS;
  332.  
  333. heightIdx = 0; // default idx in param choices must also be set
  334. widthIdx = 0;
  335. pebblesIdx = 0;
  336. lengthIdx = 1;
  337.  
  338. myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Ow process continues to run when window closes
  339.  
  340.  
  341. try // try to get previously saved game parameters from the file options<V>.controloptions
  342. {
  343. FileInputStream optionsFile = new FileInputStream("options"+Control.VERSION_NUMBER+".controloptions");
  344. ObjectInputStream OS = new ObjectInputStream(optionsFile);
  345.  
  346. nick = (String)OS.readObject();
  347. speed = OS.readInt();
  348. graphics = OS.readInt();
  349. shape = OS.readInt();
  350. boardShape = OS.readInt();
  351. widthIdx = OS.readInt();
  352. heightIdx = OS.readInt();
  353. pebblesIdx = OS.readInt();
  354. lengthIdx = OS.readInt();
  355. width = OS.readInt();
  356. height = OS.readInt();
  357. pebbles = OS.readInt();
  358. length = OS.readInt();
  359. lastAddress = (String)OS.readObject();
  360. isLite = OS.readBoolean();
  361. withHills = OS.readBoolean();
  362.  
  363. OS.close();
  364. }
  365. catch (Exception e) {} // If it doesn't work then just ignore
  366.  
  367. // Calculate my IP number
  368.  
  369. String myIP;
  370. try
  371. {
  372. myIP = InetAddress.getLocalHost().getHostAddress();
  373. }
  374. catch (IOException e) {myIP = null;}
  375.  
  376. if (myIP != null)
  377. ipLabel = new JLabel(" your IP number is " + myIP, SwingConstants.RIGHT);
  378.  
  379.  
  380. this.testServer = testServer;
  381. this.testClient = testClient;
  382.  
  383. welcome(); // go to main screen
  384. }
  385.  
  386. //-------------------------------------------------------------------------------
  387. // Main screen
  388. //-------------------------------------------------------------------------------
  389.  
  390. private void welcome ()
  391. {
  392.  
  393. thePane.removeAll(); // clear frame
  394. thePane.setLayout(new BorderLayout());
  395.  
  396.  
  397. JPanel statusPanel = new JPanel(); // two new panels
  398. JPanel buttonsPanel = new JPanel();
  399.  
  400. buttonsPanel.setLayout(new GridLayout(7,2,20,20)); // for buttons and their explanatory labels
  401.  
  402. TextArea statusArea = new TextArea(welcomeStatus,7,40,TextArea.SCROLLBARS_VERTICAL_ONLY); // intro message
  403.  
  404. buttonsPanel.add(connectLabel); // build the buttons panel
  405. buttonsPanel.add(connectPanel);
  406. buttonsPanel.add(listenLabel);
  407. buttonsPanel.add(listenPanel);
  408. buttonsPanel.add(recordLabel);
  409. buttonsPanel.add(recordPanel);
  410. buttonsPanel.add(playbackLabel);
  411. buttonsPanel.add(playbackPanel);
  412. buttonsPanel.add(optionsLabel);
  413. buttonsPanel.add(optionsPanel);
  414. buttonsPanel.add(quitLabel);
  415. buttonsPanel.add(quitPanel);
  416.  
  417.  
  418. buttonsPanel.add(Box.createRigidArea(new Dimension(0,20)));
  419.  
  420. statusArea.setEditable(false); // and the status message
  421. statusPanel.add(statusArea);
  422.  
  423. thePane.add(buttonsPanel); // build the frame
  424. thePane.add(statusPanel,BorderLayout.SOUTH);
  425. thePane.add(Box.createRigidArea(new Dimension(0, 40)),BorderLayout.NORTH);
  426. thePane.add(Box.createRigidArea(new Dimension(50, 0)), BorderLayout.WEST);
  427.  
  428. thePane.setBackground(Board.BACKGROUNDCOLOR); // make sure background is right
  429.  
  430. buttonsPanel.setBackground(Board.BACKGROUNDCOLOR);
  431. statusPanel.setBackground(Board.BACKGROUNDCOLOR);
  432.  
  433. myFrame.pack(); // display the frame
  434. myFrame.setVisible(true);
  435.  
  436. if (testServer) startListening();
  437. if (testClient) tryConnecting();
  438. }
  439.  
  440. //-------------------------------------------------------------------------------
  441. // Set game params (aka options)
  442. // We come here when user presses "options"
  443. //-------------------------------------------------------------------------------
  444.  
  445. private void options()
  446. {
  447. thePane.removeAll(); // clear frame
  448. thePane.setLayout(new GridBagLayout());
  449.  
  450. Color optionGroupColor = Board.BACKGROUNDCOLOR; // background for the options group
  451.  
  452. JPanel nickPane = new JPanel(); // the panel where nick should be input
  453. JLabel nickLabel = new JLabel("Your nickname (optional)"); // its label
  454. nickField = new JTextField(nick,10); // and text input field
  455. nickField.addActionListener(this);
  456. nickPane.setLayout(new BoxLayout(nickPane, BoxLayout.X_AXIS)); // build this panel
  457. nickPane.add(Box.createRigidArea(new Dimension(50,0)));
  458. nickPane.add(nickLabel);
  459. nickPane.add(Box.createRigidArea(new Dimension(50,0)));
  460. nickPane.add(nickField);
  461. nickPane.add(Box.createRigidArea(new Dimension(50,0)));
  462. nickPane.setBackground(Board.BACKGROUNDCOLOR);
  463.  
  464.  
  465. ButtonGroup typeGroup = new ButtonGroup(); // Group of buttons for game type
  466. if (isLite) liteButton.setSelected(true);
  467. else controlButton.setSelected(true);
  468. typeGroup.add(liteButton);
  469. typeGroup.add(controlButton);
  470. JPanel typePanel = new JPanel();
  471. typePanel.add(liteButton);
  472. typePanel.add(Box.createRigidArea(new Dimension(30,0)));
  473. typePanel.add(controlButton);
  474. typePanel.setBackground(optionGroupColor);
  475. Border typeBorder = new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Game type", TitledBorder.ABOVE_TOP, TitledBorder.LEFT);
  476. typePanel.setBorder( typeBorder);
  477.  
  478. ButtonGroup hillGroup = new ButtonGroup(); // Group of buttons for board topography
  479. if (withHills) hillsButton.setSelected(true);
  480. else noHillsButton.setSelected(true);
  481. hillGroup.add(hillsButton);
  482. hillGroup.add(noHillsButton);
  483. JPanel hillsPanel = new JPanel();
  484. hillsPanel.add(hillsButton);
  485. hillsPanel.add(Box.createRigidArea(new Dimension(30,0)));
  486. hillsPanel.add(noHillsButton);
  487. hillsPanel.setBackground(optionGroupColor);
  488. Border hillsBorder = new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Board topography", TitledBorder.ABOVE_TOP, TitledBorder.LEFT);
  489. hillsPanel.setBorder(hillsBorder);
  490.  
  491. ButtonGroup graphicGroup = new ButtonGroup(); // group of radio buttons for graphics
  492. if (graphics==1) compactButton.setSelected(true);
  493. else if (graphics==2) largeButton.setSelected(true);
  494. else hugeButton.setSelected(true);
  495. graphicGroup.add(compactButton);
  496. graphicGroup.add(largeButton);
  497. graphicGroup.add(hugeButton);
  498. JPanel graphicPanel = new JPanel();
  499. graphicPanel.add(compactButton);
  500. graphicPanel.add(Box.createRigidArea(new Dimension(30,0)));
  501. graphicPanel.add(largeButton);
  502. graphicPanel.add(Box.createRigidArea(new Dimension(30,0)));
  503. graphicPanel.add(hugeButton);
  504. graphicPanel.setBackground(optionGroupColor);
  505. Border graphicBorder = new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Graphics", TitledBorder.ABOVE_TOP, TitledBorder.LEFT);
  506. graphicPanel.setBorder(graphicBorder);
  507.  
  508. ButtonGroup boardShapeGroup = new ButtonGroup(); // group of radio buttons for board shape
  509. boardShapeGroup.add(boardSquareButton);
  510. boardShapeGroup.add(boardCircleButton);
  511. if (boardShape==1) boardSquareButton.setSelected(true);
  512. else if (boardShape==2) boardCircleButton.setSelected(true);
  513. JPanel boardShapePanel = new JPanel();
  514. boardShapePanel.add(boardSquareButton);
  515. boardShapePanel.add(Box.createRigidArea(new Dimension(30,0)));
  516. boardShapePanel.add(boardCircleButton);
  517. boardShapePanel.setBackground(optionGroupColor);
  518. Border boardShapeBorder = new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Board shape", TitledBorder.ABOVE_TOP, TitledBorder.LEFT);
  519. boardShapePanel.setBorder(boardShapeBorder);
  520.  
  521.  
  522. ButtonGroup shapeGroup = new ButtonGroup(); // group of radio buttons for area shape
  523. if (shape==1) squareButton.setSelected(true);
  524. else if (shape==2) hexButton.setSelected(true);
  525. else if (shape==3) circleButton.setSelected(true);
  526. shapeGroup.add(squareButton);
  527. shapeGroup.add(hexButton);
  528. shapeGroup.add(circleButton);
  529. JPanel shapePanel = new JPanel();
  530. shapePanel.add(squareButton);
  531. shapePanel.add(Box.createRigidArea(new Dimension(30,0)));
  532. shapePanel.add(hexButton);
  533. shapePanel.add(Box.createRigidArea(new Dimension(30,0)));
  534. shapePanel.add(circleButton);
  535. shapePanel.setBackground(optionGroupColor);
  536. Border shapeBorder = new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Shape of areas", TitledBorder.ABOVE_TOP, TitledBorder.LEFT);
  537. shapePanel.setBorder(shapeBorder);
  538.  
  539. ButtonGroup speedGroup = new ButtonGroup(); // group of radio buttons for speed
  540. if (speed==1) fastButton.setSelected(true);
  541. else if (speed==2) normalButton.setSelected(true);
  542. else slowButton.setSelected(true);
  543. speedGroup.add(fastButton);
  544. speedGroup.add(normalButton);
  545. speedGroup.add(slowButton);
  546. JPanel speedPanel = new JPanel();
  547. speedPanel.add(fastButton);
  548. speedPanel.add(Box.createRigidArea(new Dimension(30,0)));
  549. speedPanel.add(normalButton);
  550. speedPanel.add(Box.createRigidArea(new Dimension(30,0)));
  551. speedPanel.add(slowButton);
  552. speedPanel.setBackground(optionGroupColor);
  553. Border speedBorder = new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Game speed", TitledBorder.ABOVE_TOP, TitledBorder.LEFT);
  554. speedPanel.setBorder(speedBorder);
  555.  
  556. JPanel sizePanel = new JPanel(); // relative board size options
  557. heightBox.setSelectedIndex(heightIdx);
  558. widthBox.setSelectedIndex(widthIdx);
  559. pebblesBox.setSelectedIndex(pebblesIdx);
  560. sizePanel.add(widthBox);
  561. sizePanel.add(Box.createRigidArea(new Dimension(30,0)));
  562. sizePanel.add(heightBox);
  563. sizePanel.setBorder(new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Relative board dimensions", TitledBorder.ABOVE_TOP, TitledBorder.LEFT));
  564. sizePanel.setBackground(optionGroupColor);
  565.  
  566.  
  567. JPanel resourcePanel = new JPanel(); // Pebbles
  568. pebblesBox.setSelectedIndex(pebblesIdx);
  569. resourcePanel.add(pebblesBox);
  570. resourcePanel.setBorder(new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Pebbles", TitledBorder.ABOVE_TOP, TitledBorder.LEFT));
  571. resourcePanel.setBackground(optionGroupColor);
  572.  
  573. JPanel lengthPanel = new JPanel(); // game max length option
  574. lengthBox.setSelectedIndex(lengthIdx);
  575. lengthPanel.add(lengthBox);
  576. lengthPanel.setBorder(new TitledBorder(new BevelBorder(BevelBorder.RAISED), "Game length", TitledBorder.ABOVE_TOP, TitledBorder.LEFT));
  577. lengthPanel.setBackground(optionGroupColor);
  578.  
  579. JPanel buttonsPanel = new JPanel();
  580. buttonsPanel.setLayout(new GridLayout(2,2,0,20)); // for buttons and their explanatory labels
  581. buttonsPanel.add(defaultsLabel); // buttons here are defaults and return
  582. buttonsPanel.add(defaultsPanel);
  583. buttonsPanel.add(returnLabel);
  584. buttonsPanel.add(returnPanel);
  585. buttonsPanel.setBackground(Board.BACKGROUNDCOLOR);
  586.  
  587. JPanel statusPanel = new JPanel();
  588. TextArea statusArea = new TextArea(optionsStatus,5,60,TextArea.SCROLLBARS_VERTICAL_ONLY); // intro message
  589. statusArea.setEditable(false);
  590. statusPanel.setBackground(Board.BACKGROUNDCOLOR);
  591. statusPanel.add(statusArea);
  592.  
  593. // finally build the frame
  594.  
  595. GridBagConstraints constr = new GridBagConstraints();
  596. constr.insets = new Insets(10,10,10,10); // padding
  597. constr.ipadx = constr.ipady = 20;
  598.  
  599. constr.gridx = constr.gridy=0;
  600. constr.gridwidth=2;
  601. thePane.add(nickPane,constr);
  602. constr.gridx = 0; constr.gridy = 1; constr.gridwidth = 1;
  603. thePane.add(typePanel,constr);
  604. constr.gridx = 1; constr.gridy = 1;
  605. thePane.add(hillsPanel,constr);
  606. constr.gridx = 0; constr.gridy = 2;
  607. thePane.add(boardShapePanel,constr);
  608. constr.gridx = 1; constr.gridy = 2;
  609. thePane.add(shapePanel,constr);
  610. constr.gridx = 0; constr.gridy = 3;
  611. thePane.add(sizePanel,constr);
  612. constr.gridx = 1; constr.gridy = 3;
  613. thePane.add(graphicPanel,constr);
  614. constr.gridx = 0; constr.gridy = 4;
  615. thePane.add(speedPanel,constr);
  616. constr.gridx = 1; constr.gridy = 4;
  617. thePane.add(lengthPanel,constr);
  618. constr.gridx = 0; constr.gridy = 5;
  619. thePane.add(resourcePanel,constr);
  620. constr.gridx = 1; constr.gridy = 5;
  621. thePane.add(buttonsPanel,constr);
  622. constr.gridx = 0; constr.gridy = 6; constr.gridwidth=2;
  623. thePane.add(statusPanel,constr);
  624.  
  625. myFrame.pack(); // display the frame
  626. myFrame.setVisible(true);
  627.  
  628. }
  629.  
  630. //-------------------------------------------------------------------------------
  631. // Exiting options: these will have to be remembered
  632. // We come here when user exits the option screen
  633. //-------------------------------------------------------------------------------
  634.  
  635. private void exitOptions()
  636. {
  637. nick = nickField.getText();
  638.  
  639. if (fastButton.isSelected()) speed = 1;
  640. else if (normalButton.isSelected()) speed = 2;
  641. else speed = 3;
  642.  
  643. if (compactButton.isSelected()) graphics = 1;
  644. else if (largeButton.isSelected()) graphics = 2;
  645. else graphics = 3;
  646.  
  647. if (squareButton.isSelected()) shape = 1;
  648. else if (hexButton.isSelected()) shape = 2;
  649. else shape = 3;
  650.  
  651. if (boardSquareButton.isSelected()) boardShape = 1;
  652. else boardShape = 2;
  653.  
  654. if (liteButton.isSelected()) isLite = true; else isLite = false;
  655. withHills = hillsButton.isSelected();
  656.  
  657. widthIdx = widthBox.getSelectedIndex(); // also remember idx in choice lists
  658. heightIdx = heightBox.getSelectedIndex();
  659. pebblesIdx = pebblesBox.getSelectedIndex();
  660. lengthIdx = lengthBox.getSelectedIndex();
  661.  
  662. width = sizeChoices[widthIdx];
  663. height = sizeChoices[heightIdx];
  664. pebbles = sizeChoices[pebblesIdx];
  665. length = lengthChoices[lengthIdx];
  666.  
  667. writeOptions(); // save options to disc
  668. welcome(); // return to main screen
  669.  
  670. }
  671.  
  672.  
  673. private void writeOptions()
  674. {
  675. //---- and write on the options file
  676.  
  677. try
  678. {FileOutputStream optionsFile = new FileOutputStream("options"+Control.VERSION_NUMBER+".controloptions");
  679. ObjectOutputStream OS = new ObjectOutputStream(optionsFile);
  680.  
  681.  
  682. OS.writeObject(nick);
  683. OS.writeInt(speed);
  684. OS.writeInt(graphics);
  685. OS.writeInt(shape);
  686. OS.writeInt(boardShape);
  687. OS.writeInt(widthIdx);
  688. OS.writeInt(heightIdx);
  689. OS.writeInt(pebblesIdx);
  690. OS.writeInt(lengthIdx);
  691. OS.writeInt(width);
  692. OS.writeInt(height);
  693. OS.writeInt(pebbles);
  694. OS.writeInt(length);
  695. OS.writeObject(lastAddress);
  696. OS.writeBoolean(isLite);
  697. OS.writeBoolean(withHills);
  698.  
  699. OS.close();
  700. }
  701. catch (Exception e) {} // If it doesn't work then just ignore
  702.  
  703. }
  704.  
  705.  
  706. //-------------------------------------------------------------------------------
  707. // Reset Options to defaults
  708. //-------------------------------------------------------------------------------
  709.  
  710. private void defaults()
  711. { graphics = DEFAULT_GRAPHICS;
  712. shape = DEFAULT_SHAPE;
  713. boardShape = DEFAULT_BOARDSHAPE;
  714. speed = DEFAULT_SPEED;
  715. length = DEFAULT_LENGTH;
  716. height = DEFAULT_HEIGHT;
  717. width = DEFAULT_WIDTH;
  718. pebbles = DEFAULT_PEBBLES;
  719. isLite = DEFAULT_ISLITE;
  720. withHills = DEFAULT_WITHHILLS;
  721.  
  722. heightIdx = 0; // also reset choice idx:es
  723. widthIdx = 0;
  724. pebblesIdx = 0;
  725. lengthIdx = 1;
  726.  
  727. options(); // go back to options screen
  728. }
  729.  
  730. //-------------------------------------------------------------------------------
  731. // User wants to record game so choose a file
  732. //-------------------------------------------------------------------------------
  733.  
  734. private void recordGame()
  735.  
  736. {
  737. JFileChooser myFileChooser = new JFileChooser("Choose file where to save the game"); // get the file
  738. myFileChooser.setBackground(Board.BACKGROUNDCOLOR);
  739. myFileChooser.setCurrentDirectory(new File("C:/Games/Control"));
  740. if (myFileChooser.showSaveDialog(myFrame) == JFileChooser.APPROVE_OPTION)
  741. {try
  742. {record = new ObjectOutputStream(new FileOutputStream (myFileChooser.getSelectedFile()));
  743. record.writeObject("Game of Control"); // write "Game of Control" and version number on the file
  744. record.writeObject(Control.VERSION_NUMBER);}
  745. catch(Exception ex)
  746. {JOptionPane.showMessageDialog(thePane, "Sorry, unable to open and write on file.\n"+ // tell user if and why it failed
  747. "The reason given by the system is:\n"
  748. + ex.toString(),
  749. "File Error",
  750. JOptionPane.ERROR_MESSAGE);
  751. record = null;
  752. }
  753. }
  754. }
  755.  
  756. //-------------------------------------------------------------------------------
  757. // User wants to play back a recorded game so let him choose a file
  758. //-------------------------------------------------------------------------------
  759.  
  760. private void playBack()
  761. { String reason=""; // to hold reason for a failure
  762.  
  763. JFileChooser myFileChooser = new JFileChooser("Choose file to play back"); // get the file
  764. myFileChooser.setCurrentDirectory(new File("C:/Games/Control"));
  765. if (myFileChooser.showOpenDialog(myFrame) == JFileChooser.APPROVE_OPTION)
  766. try
  767. {playback = new ObjectInputStream(new FileInputStream (myFileChooser.getSelectedFile()));
  768. String s = (String) playback.readObject();
  769. String s2 = (String) playback.readObject();
  770. if (!s.equals("Game of Control")) {reason = "Not a recorded game file"; throw new Exception();} // check it begins correctly
  771. if (!s2.equals(Control.VERSION_NUMBER)) {reason = "Wrong version number of the recorded game"; throw new Exception();} // check version number
  772. }
  773. catch (Exception ex)
  774. {
  775. JOptionPane.showMessageDialog(thePane, "Sorry, unable to open and read from file.\n"+ // tell user if and why it failed
  776. "The reason is:\n"
  777. + ex.toString() + " " + reason,
  778. "File Error",
  779. JOptionPane.ERROR_MESSAGE);
  780. playback = null;
  781. }
  782. else // user clicked on cancel
  783. playback=null;
  784.  
  785. if (playback != null) // if recorded game seems OK
  786. {thePane.removeAll(); // then make this fram invisible
  787. myFrame.setVisible(false);
  788. myFrame.pack();
  789.  
  790. new PlayBack(this, playback); // and start the playback!
  791. }
  792. }
  793.  
  794. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  795. //
  796. // Client side methods: try to connect to server and set up game
  797. //
  798. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  799.  
  800.  
  801. //-------------------------------------------------------------------------------
  802. // Connect screen. User should type in an IP
  803. //-------------------------------------------------------------------------------
  804.  
  805. private void startConnecting()
  806. {
  807. thePane.removeAll(); // clear screen
  808. thePane.setLayout(new BoxLayout(thePane, BoxLayout.Y_AXIS));
  809.  
  810. JPanel adrPane = new JPanel(); // the panel where address should be input
  811. JLabel adrLabel = new JLabel("Type host name or IP"); // its label
  812. adrField = new JTextField(lastAddress,20); // and text input field
  813. adrField.addActionListener(this);
  814. adrPane.setLayout(new BoxLayout(adrPane, BoxLayout.X_AXIS)); // build this panel
  815. adrPane.add(Box.createRigidArea(new Dimension(50,0)));
  816. adrPane.add(adrLabel);
  817. adrPane.add(Box.createRigidArea(new Dimension(50,0)));
  818. adrPane.add(adrField);
  819. adrPane.add(Box.createRigidArea(new Dimension(50,0)));
  820. adrPane.setBackground(Board.BACKGROUNDCOLOR);
  821.  
  822. JPanel buttonPanel = new JPanel(); // build a button panel with abort and quit
  823. buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
  824. buttonPanel.add(abortPanel);
  825. buttonPanel.add(quitPanel);
  826.  
  827. JPanel statusPanel = new JPanel(); // build a text area explaining status
  828. TextArea statusArea = new TextArea(connectingStatus,5,35,TextArea.SCROLLBARS_VERTICAL_ONLY);
  829. statusArea.setEditable(false);
  830. statusPanel.setBackground(Board.BACKGROUNDCOLOR);
  831. statusPanel.add(statusArea);
  832.  
  833. thePane.add(Box.createRigidArea(new Dimension(0, 50))); // build the frame
  834. thePane.add(adrPane);
  835. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  836. thePane.add(buttonPanel);
  837. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  838. thePane.add(statusPanel);
  839. thePane.setBackground(Board.BACKGROUNDCOLOR); // make sure background is right
  840. buttonPanel.setBackground(Board.BACKGROUNDCOLOR);
  841.  
  842. myFrame.pack(); // show it
  843. myFrame.setVisible(true);
  844.  
  845. }
  846.  
  847.  
  848. //-------------------------------------------------------------------------------
  849. // We come here when User has typed an IP and now we should try to connect to it
  850. //-------------------------------------------------------------------------------
  851.  
  852.  
  853.  
  854. private void tryConnecting()
  855. {
  856. if (testClient)
  857. {lastAddress = "localhost";
  858. testClient = false;
  859. }
  860.  
  861. else
  862. {lastAddress = adrField.getText(); //remember what the user typed
  863. writeOptions(); // also save it in the options file
  864. adrField.setEditable(false);
  865. }
  866.  
  867. JLabel label = new JLabel("Trying to connect, please wait...");
  868. myFrame.repaint(); // for some reason this never seems to happen
  869.  
  870. boolean failed = false; // temp status variables
  871. String reason = "";
  872.  
  873. if (outSocket != null) try {outSocket.close();} catch (Exception e){} // close any remaining outsocket
  874. try{outSocket = new Socket(lastAddress, PORT_NUMBER); // set up the connection
  875. outgoing = new ObjectOutputStream (outSocket.getOutputStream());
  876. incoming = new ObjectInputStream (outSocket.getInputStream());
  877. }
  878. catch (Exception e) {failed = true; // if setting up connection failed
  879. reason = e.toString(); // remember why
  880. }
  881.  
  882. if (failed)
  883. {JOptionPane.showMessageDialog(thePane, "Sorry, unable to connect.\n"+ // tell user if and why it failed
  884. "Try again if you want\n" +
  885. "The reason given by the network is:\n"
  886. + reason,
  887. "Connection Error",
  888. JOptionPane.ERROR_MESSAGE);
  889.  
  890. adrField.setEditable(true); // Let the useer try again
  891. label.setText("Type name or IP");
  892. myFrame.repaint();
  893. }
  894.  
  895. else // connection did not fail
  896. isConnected(); // so proceed to the state where you are connected
  897. }
  898.  
  899. //-------------------------------------------------------------------------------
  900. // Connection to opponent server successful. Now negotiate game start
  901. //-------------------------------------------------------------------------------
  902.  
  903. private void isConnected()
  904. {
  905. // First show the 'is connected' screen
  906.  
  907.  
  908. thePane.removeAll(); // clear screen
  909. thePane.setLayout(new BoxLayout(thePane, BoxLayout.Y_AXIS));
  910.  
  911. JPanel adrPane = new JPanel(); // the panel where address should be input
  912. JLabel adrlab = new JLabel("Successfully connected to "+lastAddress); // its label
  913. adrPane.setLayout(new BoxLayout(adrPane, BoxLayout.X_AXIS)); // build this panel
  914. adrPane.add(Box.createRigidArea(new Dimension(50,0)));
  915. adrPane.add(adrlab);
  916. adrPane.add(Box.createRigidArea(new Dimension(50,0)));
  917. adrPane.setBackground(Board.BACKGROUNDCOLOR);
  918.  
  919. JPanel buttonPanel = new JPanel(); // build a button panel with abort and quit
  920. buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
  921. buttonPanel.add(abortPanel);
  922. buttonPanel.add(quitPanel);
  923.  
  924. JPanel statusPanel = new JPanel(); // build a text area explaining status
  925. TextArea statusArea = new TextArea(connectedStatus,7,30,TextArea.SCROLLBARS_VERTICAL_ONLY);
  926. statusArea.setEditable(false);
  927. statusPanel.add(statusArea);
  928.  
  929. thePane.add(Box.createRigidArea(new Dimension(0, 50))); // build the frame
  930. thePane.add(adrPane);
  931. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  932. thePane.add(buttonPanel);
  933. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  934. thePane.add(statusPanel);
  935.  
  936. thePane.setBackground(Board.BACKGROUNDCOLOR); // make sure background is right
  937. buttonPanel.setBackground(Board.BACKGROUNDCOLOR);
  938. statusPanel.setBackground(Board.BACKGROUNDCOLOR);
  939.  
  940. myFrame.pack(); // show it
  941. myFrame.setVisible(true);
  942.  
  943. // Tell opponent I want to play and my params
  944.  
  945. String reason=""; // temp status varaibles
  946.  
  947. boolean failed = false;
  948.  
  949. playWhite = new java.util.Random().nextFloat() < 0.5; // randomly select my color for play
  950.  
  951. try{
  952. outgoing.writeObject("Game of control"); // First message to say what is going on
  953. outgoing.writeObject( // then tell my params
  954. new GameParameters(Control.VERSION_NUMBER, graphics, isLite, withHills, nick, shape, boardShape, speed, width, height, pebbles, length));
  955.  
  956. outgoing.writeObject(playWhite ? "I play white" // tell who plays white
  957. : "You play white");
  958.  
  959. getAccept = new GetAccept(); // start a new thread to listen for reply
  960. getAccept.start();
  961. }
  962. catch (Exception e){failed = true; reason = e.toString(); // if IOfailure remember why
  963. }
  964. if (failed)
  965. {JOptionPane.showMessageDialog(thePane, "Sorry, unable to start game\n"+ // Tell if and why it failed
  966. "Try again if you want\n" +
  967. "The reason is:\n"
  968. + reason,
  969. "Connection Error",
  970. JOptionPane.ERROR_MESSAGE);
  971.  
  972. welcome(); // if failed then return to main screen
  973. }
  974.  
  975. }
  976.  
  977. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  978. //
  979. // Inner class GetAccept: a thread to get a reply from a challenge
  980. //
  981. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  982.  
  983. private class GetAccept extends Thread
  984. {
  985. private boolean aborted = false;
  986. private boolean failed = false;
  987.  
  988. private String reply, reason;
  989.  
  990.  
  991. ///--------------------------------- To make me stop-------------------------------------
  992.  
  993.  
  994. void abort()
  995. {aborted = true;
  996. }
  997.  
  998.  
  999. ///--------------------------------- Start of this thread---------------------------------
  1000.  
  1001. @Override
  1002. public void run()
  1003.  
  1004. {try
  1005. {reply = (String)incoming.readObject();} // try reading something from opponent
  1006. catch (Exception e){failed = true; reason = e.toString();} // if it failed remember why
  1007.  
  1008. if (reason == null) reason = reply; // default fail reason is a text transmitted by opponent ( eg mismatch params)
  1009.  
  1010. if (!aborted) // only continue if I should
  1011. if (failed || !reply.equals("OK Game of control")) // if something amiss then inform user
  1012. {JOptionPane.showMessageDialog(thePane, "Game refused\n" +
  1013. "The contact would not start the game.\n" +
  1014. "The reason is:\n"+
  1015. reason
  1016. ,
  1017. "Game Error", JOptionPane.ERROR_MESSAGE);
  1018. abortIt(); // and tell my parent to stop this attempt
  1019.  
  1020.  
  1021. }
  1022. else // read did not fail and game seems OK
  1023.  
  1024. {try
  1025. {opponentNick = (String)incoming.readObject();} // read opponent's nick
  1026. catch (Exception e) {failed = true; reason = e.toString();} // if that fails I just ignore it
  1027. startGame(); // in any case tell parent to go ahead with game
  1028. } // end else
  1029. }
  1030. }
  1031.  
  1032. //-------------------------------------------------------------------------------
  1033. // All is ready, so start the game. We come here when getAccept invokes the method
  1034. // after having receivved an accept to play from opponent
  1035. //-------------------------------------------------------------------------------
  1036.  
  1037. private void startGame()
  1038. {
  1039. thePane.removeAll(); // get rid of the frame
  1040. myFrame.setVisible(false);
  1041. myFrame.pack();
  1042.  
  1043. ControlTimer controlTimer = new ControlTimer(playWhite); // get new control timer for the player
  1044.  
  1045. if (record != null) // if recording then write game params on the record
  1046. try {record.writeObject(
  1047. new RecordedGame(Control.VERSION_NUMBER, isLite, withHills, graphics, playWhite?nick:opponentNick, playWhite?opponentNick:nick,
  1048. shape, boardShape, width, height, pebbles, length));}
  1049. catch (Exception e)
  1050. {JOptionPane.showMessageDialog(thePane, "Sorry, unable to record.\n"+ // if this fail tell user
  1051. "The reason is:\n"
  1052. + "no response for five seconds",
  1053. "File Error",
  1054. JOptionPane.ERROR_MESSAGE);
  1055. record = null; // and cease trying to record
  1056. }
  1057.  
  1058. Player player = new Player(isLite, withHills, playWhite, controlTimer, incoming, outgoing, record, graphics, shape, boardShape,
  1059. height, width, pebbles, speed, length); // Set up the player
  1060.  
  1061.  
  1062. new PlayerInterface (player, controlTimer, this, nick, opponentNick); // and the interface
  1063.  
  1064. }
  1065.  
  1066.  
  1067. //-------------------------------------------------------------------------------
  1068. // Come here when a game has ended
  1069. //-------------------------------------------------------------------------------
  1070.  
  1071. void playAgain()
  1072. {
  1073. abortIt(); // release everything and go to main screen
  1074. }
  1075.  
  1076. //-------------------------------------------------------------------------------
  1077. // Come here when you press "abort" instead of accepting to play
  1078. //-------------------------------------------------------------------------------
  1079.  
  1080.  
  1081. private void decline() {
  1082. try {
  1083. outgoing.writeObject("Opponent declines to play"); // Inform the opponent that you decline
  1084. } catch (Exception ex) {}
  1085. abortIt();
  1086. }
  1087.  
  1088.  
  1089.  
  1090. //-------------------------------------------------------------------------------
  1091. // Release everything and go to main screen
  1092. //-------------------------------------------------------------------------------
  1093.  
  1094.  
  1095. private void abortIt()
  1096.  
  1097. { if (getAccept != null) getAccept.abort(); // abort any threads listening for communication
  1098. if (getConnection != null) getConnection.abort();
  1099. getAccept = null;
  1100. getConnection = null;
  1101.  
  1102. try{
  1103. if (incoming != null) incoming.close(); // close all streams
  1104. if (inSocket != null) inSocket.close();
  1105. if (outgoing != null) outgoing.close();
  1106. if (connection != null) connection.close();
  1107. if (outSocket != null) outSocket.close();
  1108. } catch (Exception ex){}
  1109.  
  1110.  
  1111. outgoing = null;
  1112. incoming = null;
  1113. connection = null;
  1114. outSocket = null;
  1115. inSocket=null;
  1116.  
  1117. if (record != null) try {record.close();} catch (Exception e){}
  1118. record = null;
  1119.  
  1120. welcome(); // go to main screen
  1121. }
  1122.  
  1123.  
  1124. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1125. //
  1126. // Server side methods: listen for a connection where to play the game
  1127. //
  1128. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1129.  
  1130.  
  1131.  
  1132. //-------------------------------------------------------------------------------
  1133. // Set up listening screen
  1134. //-------------------------------------------------------------------------------
  1135.  
  1136.  
  1137. private void startListening()
  1138. {
  1139. thePane.removeAll(); // clear screen
  1140. thePane.setLayout(new BoxLayout(thePane, BoxLayout.Y_AXIS));
  1141.  
  1142.  
  1143. JPanel buttonsPanel = new JPanel();
  1144. buttonsPanel.setLayout(new GridLayout(3,2,20,40));
  1145. buttonsPanel.add(abortLabel); // set up the buttons
  1146. buttonsPanel.add(abortPanel);
  1147. buttonsPanel.add(quitLabel);
  1148. buttonsPanel.add(quitPanel);
  1149. if (ipLabel != null) buttonsPanel.add(ipLabel);
  1150.  
  1151. JPanel statusPanel = new JPanel();
  1152. TextArea statusArea = new TextArea(listenStatus,7,40,TextArea.SCROLLBARS_VERTICAL_ONLY); // message about listening
  1153. statusArea.setEditable(false);
  1154. statusPanel.add(statusArea);
  1155.  
  1156. thePane.add(Box.createRigidArea(new Dimension(0, 30)));
  1157. thePane.add(buttonsPanel); // set up the screen
  1158. thePane.add(Box.createRigidArea(new Dimension(0, 30)));
  1159. thePane.add(statusPanel);
  1160.  
  1161. thePane.setBackground(Board.BACKGROUNDCOLOR); // make sure background is OK
  1162. buttonsPanel.setBackground(Board.BACKGROUNDCOLOR);
  1163. statusPanel.setBackground(Board.BACKGROUNDCOLOR);
  1164.  
  1165. myFrame.pack(); // show screen
  1166. myFrame.setVisible(true);
  1167. myFrame.invalidate();
  1168. myFrame.repaint();
  1169.  
  1170.  
  1171. getConnection = new GetConnection(); // Listen for connection in a new thread!
  1172. getConnection.start(); // The reason is that startListening is invoked by
  1173. // a button click and therefore runs on the
  1174. // AWT thread. We must NEVER put this thread to sleep,
  1175. // so it cannot wait at an input.
  1176. }
  1177.  
  1178. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1179. //
  1180. // Inner class GetConnection: a thread which tries to establish connection on the server side
  1181. //
  1182. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1183.  
  1184. private class GetConnection extends Thread
  1185.  
  1186. {
  1187.  
  1188. private boolean aborted = false; // to kill me off
  1189.  
  1190.  
  1191. /// --------------------------------- Method to stop this thread
  1192.  
  1193.  
  1194. void abort(){aborted=true;}
  1195.  
  1196. ///--------------------------------- Start of this thread
  1197.  
  1198. @Override
  1199. public void run()
  1200. {
  1201. boolean failed = false; // temp status variables
  1202. aborted = false;
  1203.  
  1204. String reason = "";
  1205. GameParameters gp = null;
  1206.  
  1207. if (inSocket == null) // only if we have not done this before
  1208. {try {inSocket = new ServerSocket(StartControl.PORT_NUMBER);} // create a server socket
  1209. catch (Exception e)
  1210. {failed = true; // if that does not work tell user
  1211.  
  1212. JOptionPane.showMessageDialog(thePane, "Sorry, unable to open server\n"+
  1213. "It seems you have another server listening at this port\n" +
  1214. "Try again if you want, by pressing Abort\n"+
  1215. "The reason given by the network is:\n"+
  1216. (e.toString()),
  1217. "Server Error", JOptionPane.ERROR_MESSAGE);
  1218.  
  1219. }
  1220. }
  1221.  
  1222. if (!failed & !aborted) // Server created successfully
  1223.  
  1224. {
  1225. try // try to set up connection
  1226. {
  1227. failed = false;
  1228. connection = inSocket.accept(); // get connection and streams
  1229. incoming = new ObjectInputStream(connection.getInputStream());
  1230. outgoing = new ObjectOutputStream(connection.getOutputStream());
  1231.  
  1232. if (!((String)incoming.readObject()).equals("Game of control")) // check if it is someone who wants to play
  1233. {failed = true; reason = "someone made contact but not to play Control";}
  1234.  
  1235. gp = (GameParameters)(incoming.readObject());
  1236.  
  1237. if (!gp.versionNumber.equals(Control.VERSION_NUMBER))
  1238. {failed = true; reason = "Wrong version of the game";} // check that we all agree on game parameters
  1239. if (gp.graphics != graphics)
  1240. {failed=true; reason="Different graphic sizes";}
  1241. if (gp.shape != shape)
  1242. {failed = true; reason = "Mismatching area shapes";}
  1243. if (gp.boardShape != boardShape)
  1244. {failed = true; reason = "Mismatching board shape";}
  1245. if (gp.isLite != isLite)
  1246. {failed = true; reason = "Only one of us wants ControLITE";}
  1247. if (gp.withHills != withHills)
  1248. {failed = true; reason = "Only one of us wants to play with hills";}
  1249. if (gp.width != width)
  1250. {failed = true; reason = "Mismatching board width";}
  1251. if (gp.height != height)
  1252. {failed = true; reason = "Mismatching board height";}
  1253. if (gp.pebbles != pebbles)
  1254. {failed = true; reason = "Mismatching number of pebbles";}
  1255. if (gp.speed != speed)
  1256. {failed = true; reason = "Mismatching game speed";}
  1257. if (gp.length != length)
  1258. {failed = true; reason = "Mismatching game length";}
  1259.  
  1260. Object o = incoming.readObject(); // opponent decides who should play white
  1261.  
  1262. if (((String)o).equals("I play white")) playWhite = false;
  1263. else
  1264. if (((String)o).equals("You play white")) playWhite = true;
  1265. else {failed = true; reason = "someone contacted me but then quit";}
  1266.  
  1267. }
  1268. catch (Exception e) {failed = true; reason = e.toString();} // if IO failed remember why
  1269.  
  1270. if (failed &!aborted ) // if failed tell opponent why
  1271. {JOptionPane.showMessageDialog(thePane, "Incoming connection refused\n" +
  1272. "Someone tried to make contact but we could not start the game.\n" +
  1273. "The reason is:\n"+
  1274. reason,
  1275. "Game Error", JOptionPane.ERROR_MESSAGE);
  1276. try {outgoing.writeObject(reason);} catch (Exception e){}
  1277. finally {abortIt();}
  1278. }
  1279.  
  1280. }
  1281.  
  1282. if (!failed & !aborted)
  1283. {opponentNick = gp.nick;
  1284. // all is fine, so
  1285. checkGame(); // let user decide if user wants to play
  1286. }
  1287. } // end run
  1288.  
  1289.  
  1290. }
  1291.  
  1292.  
  1293. //-------------------------------------------------------------------------------
  1294. // Screen where user decides whether to play an opponent. We come here when getConnection
  1295. // invokes it after receiving a connect request.
  1296. //-------------------------------------------------------------------------------
  1297.  
  1298.  
  1299. private void checkGame()
  1300.  
  1301. {
  1302. thePane.removeAll(); // clear screen
  1303. thePane.setLayout(new BoxLayout(thePane,BoxLayout.Y_AXIS));
  1304.  
  1305. String hostname = connection.getInetAddress().getHostName(); // host name of remote opponent
  1306. JLabel hostLabel;
  1307. if (opponentNick.equals(""))
  1308. hostLabel = new JLabel("Anonymous opponent wants to play from "+hostname,SwingConstants.CENTER); // label displaying the name
  1309. else hostLabel = new JLabel(opponentNick+" wants to play from "+hostname,SwingConstants.CENTER); // label displaying the name
  1310. hostLabel.setBackground(Board.BACKGROUNDCOLOR);
  1311. JPanel hostPanel = new JPanel();
  1312. hostPanel.add(hostLabel);
  1313. hostPanel.setBackground(Board.BACKGROUNDCOLOR);
  1314.  
  1315. JPanel buttonsPanel = new JPanel();
  1316. buttonsPanel.setLayout(new GridLayout(3,2,20,40));
  1317. buttonsPanel.add(playLabel);
  1318. buttonsPanel.add(playPanel);
  1319. buttonsPanel.add(abortLabel);
  1320. buttonsPanel.add(abortPanel);
  1321. buttonsPanel.add(quitLabel);
  1322. buttonsPanel.add(quitPanel);
  1323.  
  1324. JPanel statusPanel = new JPanel();
  1325. TextArea statusArea = new TextArea(acceptingstatus,5,40,TextArea.SCROLLBARS_VERTICAL_ONLY);
  1326. statusArea.setEditable(false);
  1327. statusPanel.add(statusArea);
  1328.  
  1329. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  1330. thePane.add(hostPanel);
  1331. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  1332. thePane.add(buttonsPanel);
  1333. thePane.add(Box.createRigidArea(new Dimension(0, 50)));
  1334. thePane.add(statusPanel);
  1335.  
  1336. thePane.setBackground(Board.BACKGROUNDCOLOR);
  1337. buttonsPanel.setBackground(Board.BACKGROUNDCOLOR);
  1338. statusPanel.setBackground(Board.BACKGROUNDCOLOR);
  1339.  
  1340.  
  1341. myFrame.pack();
  1342. myFrame.setVisible(true);
  1343.  
  1344. if (testServer)
  1345. {testServer = false;
  1346. playGame();
  1347. }
  1348. }
  1349.  
  1350. //-------------------------------------------------------------------------------
  1351. // User has pressed Play, so tell opponent and get going
  1352. //-------------------------------------------------------------------------------
  1353.  
  1354. private void playGame()
  1355. { boolean failed = false; // temp
  1356.  
  1357. try {outgoing.writeObject("OK Game of control");
  1358. outgoing.writeObject(nick); } // tell opponent I am willing to play, and my nick
  1359. catch (Exception e)
  1360. { // Oops, opponent dropped out
  1361. failed = true;
  1362. JOptionPane.showMessageDialog(thePane, "Game start failed\n" +
  1363. "Connection to your opponent disappeared\n" +
  1364. "The reason given by the network is:\n"+
  1365. e.toString() ,
  1366. "Game Error", JOptionPane.ERROR_MESSAGE);
  1367. abortIt();
  1368. }
  1369. if (!failed) startGame(); // all is well so game starts
  1370. }
  1371.  
  1372.  
  1373.  
  1374. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1375. //
  1376. // Action listener
  1377. //
  1378. // Here is defined what happens when buttons are pressed
  1379. //
  1380. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1381.  
  1382. public void actionPerformed(ActionEvent e)
  1383. {
  1384. if (e.getSource() == quitButton) // quit: close server and leave
  1385. {if (inSocket != null) try {inSocket.close();} catch (Exception ex) {}
  1386. System.exit(0);}
  1387.  
  1388. if (e.getSource() == connectButton) startConnecting();
  1389.  
  1390. if (e.getSource() == listenButton) startListening();
  1391.  
  1392. if (e.getSource() == abortButton) decline();
  1393.  
  1394. if (e.getSource() == recordButton) recordGame();
  1395.  
  1396. if (e.getSource() == playbackButton) playBack();
  1397.  
  1398. if (e.getSource() == optionsButton) options();
  1399.  
  1400. if (e.getSource() == defaultsButton) defaults();
  1401.  
  1402. if (e.getSource() == returnButton) exitOptions();
  1403.  
  1404. if (e.getSource() == playButton) playGame();
  1405.  
  1406. if (e.getSource() == adrField) tryConnecting();
  1407.  
  1408. }
  1409.  
  1410. } // Here ends class StartControl
  1411.  
  1412. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1413.  
  1414.  
  1415. //---------------------------------------------------------------------------------
  1416. // Wrapper for game parameters transmitted between players
  1417. //---------------------------------------------------------------------------------
  1418.  
  1419.  
  1420. class GameParameters implements Serializable {
  1421.  
  1422. String versionNumber; // game version number
  1423.  
  1424. // game params chosen in options
  1425.  
  1426. boolean isLite; // true if controlite, false if control
  1427. boolean withHills; // with hills on board
  1428. int graphics; // graphics size
  1429. String nick; // nick of sender
  1430. int shape; // area shape
  1431. int boardShape; // board shape
  1432. int speed; // game speed
  1433. int width; // board relative width
  1434. int height; // board relative height
  1435. int pebbles; // pebbles per player
  1436. int length; // game length
  1437.  
  1438. //---------------- constructor just sets the fields-------------------------------
  1439.  
  1440.  
  1441. public GameParameters(String v, int gr, boolean il, boolean wH, String n, int g,
  1442. int bS, int s, int w, int h, int p, int l)
  1443. {versionNumber=v; graphics=gr; isLite=il; withHills = wH; nick=n; shape = g;
  1444. boardShape = bS; speed=s; width=w; height=h; pebbles=p; length=l;
  1445. }
  1446. }
  1447.  
  1448.  
  1449. //---------------------------------------------------------------------------------
  1450. // Wrapper for game parameters on a recorded game
  1451. //---------------------------------------------------------------------------------
  1452.  
  1453. class RecordedGame implements Serializable
  1454.  
  1455. {String versionNumber; // game version number
  1456. boolean isLite; // true if controlite, false if control
  1457. boolean withHills; // with hills on board
  1458. int graphics; // graphics size
  1459. String whiteNick; // white's nick
  1460. String blackNick; // black's nick
  1461. int shape; // area shape
  1462. int boardShape; // board shape
  1463. int width; // game params
  1464. int height; // board relative height
  1465. int pebbles; // pebbles per player
  1466. int length; // game length
  1467.  
  1468. //---------------- constructor just sets the fields-------------------------------
  1469.  
  1470. public RecordedGame (String v, boolean il, boolean wH, int gr, String w, String b,
  1471. int g, int bS, int wi, int h, int p, int l)
  1472. {versionNumber = v; isLite=il; withHills = wH; graphics = gr; whiteNick = w; blackNick = b;
  1473. shape = g; boardShape = bS; width = wi; height = h; pebbles = p; length = l;}
  1474. }
  1475.  
  1476.  
  1477.  
  1478.  
  1479.  
  1480. /**
  1481. *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1482. *
  1483. * ControlTimer
  1484. *
  1485. * This is the timer for determining when a player is allowed to move
  1486. *
  1487. *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1488. */
  1489.  
  1490. class ControlTimer extends JPanel implements ActionListener
  1491.  
  1492. {
  1493.  
  1494. private static final Color BACKGROUNDCOLOR = new Color(160,180,120); // Background color for the timer (currently same as for board)
  1495.  
  1496. // Various sizing constants for graphics
  1497.  
  1498.  
  1499. private static final int TOP_OFFSET = 0; // Offset from top border
  1500. static final int BAR_HEIGHT = 25; // timer height
  1501. private int LEFT_OFFSET; // Offset from left border
  1502. private int BAR_LENGTH; // timer bar length
  1503.  
  1504. private final int UPDATE_FREQUENCY = 20; // how often to update in milliseconds
  1505.  
  1506.  
  1507. private Timer timer; // The timer
  1508.  
  1509. private Player player; // Whom to tell when timer expires
  1510.  
  1511. private boolean whiteColor; // true if timer should be painted white
  1512.  
  1513. private float remainingFraction = 0f; // remaining fraction of time
  1514.  
  1515. private long startTime; // Epoch time in ms when timer started
  1516.  
  1517. private int totalTime; // total time in ms
  1518.  
  1519. private int size, width; // board parameters (needed so the timer will fit nicely)
  1520.  
  1521.  
  1522. //------------------------------------------------------------------------------------
  1523. // Create a controlTimer. Just done once for each player.
  1524. //------------------------------------------------------------------------------------
  1525.  
  1526. ControlTimer(boolean whiteColor) {
  1527. super(); // this is also a JPanel
  1528. this.whiteColor = whiteColor;
  1529. }
  1530.  
  1531. //------------------------------------------------------------------------------------
  1532. // Accept a message from my Player to initiate variables
  1533. //------------------------------------------------------------------------------------
  1534.  
  1535. void getPlayer(Player player, // my player
  1536. int size, // size of one board square in pixels
  1537. int width) // board width in number of squares
  1538. { // This is how the player tells me who he is so I can report timeouts
  1539. this.player = player;
  1540. this.size = size; // he also tells me how big the board is
  1541. this.width = width; // so I set the sizing constants from this
  1542. LEFT_OFFSET = size;
  1543. BAR_LENGTH = size*width;
  1544. }
  1545.  
  1546. //------------------------------------------------------------------------------------
  1547. // Paint the timer
  1548. //------------------------------------------------------------------------------------
  1549.  
  1550. @Override
  1551. public void paintComponent(Graphics page)
  1552. {super.paintComponent(page); // This is also a JPanel
  1553.  
  1554. page.setColor(BACKGROUNDCOLOR); // Paint background
  1555. page.fillRect(0,0, size*(width+2), 30);
  1556.  
  1557. // Paint timer bar
  1558.  
  1559. if (whiteColor) page.setColor(Color.white); else page.setColor(Color.black);
  1560. page.fillRect(LEFT_OFFSET, TOP_OFFSET,
  1561. (int)(remainingFraction*BAR_LENGTH),
  1562. BAR_HEIGHT);
  1563. }
  1564.  
  1565. //----------------------------------------------------------------------
  1566. // Start timer with totalTime in seconds
  1567. //-----------------------------------------------------------------------
  1568.  
  1569.  
  1570. void startIt(float totalTime)
  1571. {
  1572. startTime = System.currentTimeMillis(); // remember when it started
  1573. remainingFraction = 1f; // at start all the time is remaining
  1574. this.totalTime = (int)(1000*totalTime); // total time in ms
  1575. timer = new Timer(UPDATE_FREQUENCY, this); // create a new Timer reporting to me
  1576. repaint(); // draw me, for the first time
  1577.  
  1578. timer.start(); // start the timer
  1579. }
  1580.  
  1581. //----------------------------------------------------------------------
  1582. // Interrupt from the timer
  1583. //-----------------------------------------------------------------------
  1584.  
  1585. public void actionPerformed(ActionEvent e)
  1586.  
  1587. {
  1588. remainingFraction = 1 - (float)(System.currentTimeMillis()- startTime)/(float)totalTime; // Fraction of total time that remains
  1589. if (remainingFraction < 0) remainingFraction = (float)0; // (should neve be negative)
  1590.  
  1591. if (remainingFraction<=0) { // If finished then stop timer and notify player
  1592. timer.stop();
  1593. repaint();
  1594. player.timeOut();
  1595. }
  1596. else repaint(); // OW just repaint the timer bar
  1597.  
  1598.  
  1599. }
  1600.  
  1601.  
  1602. //----------------------------------------------------------------------
  1603. // Stop the timer
  1604. //-----------------------------------------------------------------------
  1605.  
  1606. void stopIt()
  1607. {if (timer != null) timer.stop();
  1608. }
  1609.  
  1610.  
  1611. }
  1612.  
  1613.  
  1614. /*
  1615. ************************************************************************************************
  1616. *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1617. *Create the graphic interface for a player:
  1618. *set up a frame with panels
  1619. *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1620. ***********************************************************************************************
  1621. */
  1622.  
  1623. class PlayerInterface implements ActionListener
  1624.  
  1625. {
  1626. private StartControl startControl; // Who started me and to whom I report termination
  1627.  
  1628. private JFrame myFrame; // my Frame
  1629. private final JPanel tops = new JPanel(); // top panel will hold the quit button and player ids
  1630. private final JPanel quitPanel = new JPanel(); // part of topside to hold quit button
  1631. private final JPanel bottoms = new JPanel(); // bottom panel will hold the control timer
  1632. private final JButton quitButton = new JButton("Quit"); // the quit button
  1633.  
  1634. private Player player; // my player
  1635.  
  1636. // various temp variables.
  1637. private boolean running = true; // true if player is running.
  1638. private boolean noids; // true if neither player has a nick
  1639.  
  1640. private String whiteNick, blackNick; // nick of white and of black resp.
  1641.  
  1642. //----------------------------------------------------------------------------------------------------------------------
  1643. // Constructor does most of the work to set things up
  1644. //----------------------------------------------------------------------------------------------------------------------
  1645.  
  1646. PlayerInterface(Player player, // Who will actually play the game
  1647. ControlTimer timer, // and its control timer. These are the ones I should display properly.
  1648. StartControl startControl, // Parent to which I report termination
  1649. String nick, // nick my player uses
  1650. String opponentNick) // nick the opponent uses
  1651. {
  1652.  
  1653. this.player = player; // begin by remembering parameters
  1654. this.startControl = startControl;
  1655.  
  1656. myFrame = new JFrame("Playing the game of Control" +
  1657. (player.isLite ? "ite" : ""));
  1658.  
  1659.  
  1660. myFrame.getContentPane().setLayout(new BorderLayout()); // with borderlayout manager
  1661. myFrame.getContentPane().setBackground(Board.BACKGROUNDCOLOR);// and proper background
  1662.  
  1663. myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Ow process continues when window closes
  1664.  
  1665. bottoms.setLayout(new BoxLayout(bottoms, BoxLayout.X_AXIS)); // Bottom region reserved for the timer
  1666. bottoms.add(Box.createRigidArea(new Dimension(0,ControlTimer.BAR_HEIGHT))); // (ow the layout manager will reserve no space)
  1667. bottoms.add(timer);
  1668.  
  1669. myFrame.getContentPane().add (player); // central region for the board
  1670. myFrame.getContentPane().add(bottoms,BorderLayout.SOUTH);
  1671.  
  1672. quitButton.setBackground(Color.red); // put the quit button topside
  1673. quitPanel.add(quitButton);
  1674. quitPanel.setBackground(Board.BACKGROUNDCOLOR);
  1675. tops.setLayout(new BoxLayout(tops,BoxLayout.Y_AXIS));
  1676. tops.add(quitPanel);
  1677. // topside also get to print player nicks
  1678. // unless neither player has a nick
  1679. tops.add(Box.createRigidArea(new Dimension(0, 20)));
  1680.  
  1681. if (player.playWhite) // set whitenick and blacknick
  1682. {whiteNick = nick; blackNick = opponentNick;}
  1683. else
  1684. {whiteNick = opponentNick; blackNick = nick;}
  1685.  
  1686. noids = ((whiteNick.equals(""))&(blackNick.equals(""))); // set noids true iff neither player has a nick
  1687.  
  1688. if (whiteNick.equals("")) whiteNick="White"; // default nicks for white and black
  1689. if (blackNick.equals("")) blackNick="Black";
  1690.  
  1691. if (!noids) tops.add(buildIdPanel()); // display the nicks in idPanel unless both empty
  1692.  
  1693. quitButton.addActionListener(this); // I listen for quit button myself
  1694. myFrame.getContentPane().add(tops,BorderLayout.NORTH);
  1695. tops.setBackground(Board.BACKGROUNDCOLOR);
  1696.  
  1697. myFrame.pack();
  1698. myFrame.setVisible(true);
  1699. focus();
  1700.  
  1701. player.setInterface(this); // tell my player who I am, so it can report game termination properly
  1702.  
  1703. }
  1704.  
  1705. //------------------------------------------------------------------------
  1706. // Strangely, this is necessary in order to let the player listen for key actions
  1707. //------------------------------------------------------------------------
  1708.  
  1709. private void focus()
  1710. { player.requestFocus();
  1711. }
  1712.  
  1713. //------------------------------------------------------------------------
  1714. // Build a panel displaying player nicks and add it to tops
  1715. //------------------------------------------------------------------------
  1716.  
  1717. private JPanel buildIdPanel()
  1718. {
  1719. JPanel idPanel = new JPanel();
  1720. idPanel.setLayout(new BoxLayout(idPanel, BoxLayout.X_AXIS));
  1721.  
  1722. JLabel whiteLabel = new JLabel(whiteNick);
  1723. whiteLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
  1724. whiteLabel.setForeground(Color.white);
  1725. whiteLabel.setBackground(Board.BACKGROUNDCOLOR);
  1726.  
  1727. JLabel vsLabel = new JLabel(" vs ");
  1728. vsLabel.setFont(new Font("SansSerif", Font.BOLD, 16));
  1729. vsLabel.setForeground(Color.orange);
  1730. vsLabel.setBackground(Board.BACKGROUNDCOLOR);
  1731.  
  1732. JLabel blackLabel = new JLabel(blackNick);
  1733. blackLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
  1734. blackLabel.setForeground(Color.black);
  1735. blackLabel.setBackground(Board.BACKGROUNDCOLOR);
  1736.  
  1737. idPanel.add(whiteLabel);
  1738. idPanel.add(vsLabel);
  1739. idPanel.add(blackLabel);
  1740. idPanel.setBackground(Board.BACKGROUNDCOLOR);
  1741.  
  1742. return idPanel;
  1743.  
  1744. }
  1745. //-----------------------------------------------------------------------
  1746. // When game has ended the display needs change
  1747. //-----------------------------------------------------------------------
  1748.  
  1749. void endGame(String reason, // reason to present to the user
  1750. String recordReason) // reason to record on file if game is recorded
  1751.  
  1752. { // Player.releaseLock();
  1753. myFrame.getContentPane().remove(tops); // perhaps unnecessary since borderlayout is used in myFrame
  1754. tops.removeAll(); // but the topside area needs to be redone
  1755. tops.setLayout(new BoxLayout(tops, BoxLayout.Y_AXIS));
  1756.  
  1757. JLabel reasonLabel = new JLabel(reason, SwingConstants.CENTER); // message to user upon termination
  1758. reasonLabel.setBackground(Color.orange);
  1759. reasonLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
  1760.  
  1761.  
  1762. tops.add(Box.createRigidArea(new Dimension(0,20))); // display this message and the quit button topside
  1763. tops.add(quitPanel);
  1764. tops.add(Box.createRigidArea(new Dimension(0,20)));
  1765. tops.add(reasonLabel);
  1766. tops.add(Box.createRigidArea(new Dimension(0,20)));
  1767.  
  1768. if (!noids) tops.add(buildIdPanel()); // display player nicks
  1769.  
  1770. myFrame.getContentPane().add(tops,BorderLayout.NORTH); // put on display
  1771.  
  1772. player.endGame(recordReason); // tell my player to clean up
  1773.  
  1774. myFrame.pack(); // show it
  1775. myFrame.repaint();
  1776. running = false;
  1777. }
  1778.  
  1779. //---------------------------------------------------------------------------------
  1780. // Termination. get rid of the frame and tell my parent
  1781. //---------------------------------------------------------------------------------
  1782.  
  1783. private void quit()
  1784. { myFrame.dispose();
  1785. startControl.playAgain();
  1786. }
  1787.  
  1788.  
  1789. //--------------------------------------------------------------------------------
  1790. // I am listening for actions myself
  1791. //---------------------------------------------------------------------------------
  1792.  
  1793.  
  1794. public void actionPerformed(ActionEvent e)
  1795. {
  1796. if (e.getSource() == quitButton) // if quit button is pressed
  1797. {if (running) player.quit(); // if player is running then player has to quit
  1798. else quit();} // ow I have to quit
  1799. }
  1800.  
  1801. } //--------------------------- end of player interface -------------------------------------------------
  1802.  
  1803.  
  1804.  
  1805. /*
  1806. *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1807. *
  1808. *Main class in the game of control
  1809. *
  1810. *Serves one player with game logics
  1811. *Reacts to player mouse commands , sends moves to opponent,
  1812. * and reacts to to moves received by opponent
  1813. *
  1814. *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1815. *
  1816. */
  1817.  
  1818.  
  1819.  
  1820. class Player extends JPanel implements ActionListener
  1821. {
  1822.  
  1823.  
  1824. //----------- player activity state ---------------------------------------------------------------------
  1825.  
  1826. private Piece currentPiece = null; // Piece over which mouse was pressed
  1827. private Piece currentSelection = null; // Selection for promotions
  1828. private Board.Position currentPos; // where the mouse is
  1829. private boolean waiting; // True if Timer not expired so I must wait
  1830. private boolean clockHasExpired = false; // true when game clock has expired and game is over
  1831.  
  1832. //----------- game activity state -----------------------------------------------------------------------
  1833.  
  1834. int clock = 0; // elapsed time in seconds
  1835.  
  1836. private int messageNumber = 0; // number communication messages incrementally
  1837. private int ackedNumber = -1; // last acknowledged message to opponent
  1838.  
  1839. //----------- game parameters (should not change during a game) -----------------------------------------
  1840.  
  1841. boolean playWhite; // true if currently playing white
  1842.  
  1843. private Board board; // the board for this player
  1844.  
  1845. private ControlTimer controlTimer; // my Controltimer
  1846. private PlayerInterface myInterface; // my Interface (runs the quit button etc)
  1847. private MoveListener moveListener; // My listener for moves by opponent
  1848.  
  1849. private Timer timer; // oneSecondTimer for the game clock
  1850.  
  1851. private ObjectOutputStream outgoing; // for sending moves to opponent
  1852. private ObjectInputStream incoming;
  1853. private ObjectOutputStream record; // stream to recording file
  1854.  
  1855. private int GAME_LENGTH; // Game length in seconds
  1856. private float timeBase; // number of seconds for one time unit (nimbler move)
  1857.  
  1858. boolean isLite; // true if playing controlite
  1859. boolean withHills; // true if hills on the board
  1860.  
  1861. private Semaphore mutex; // This protects critical sections updating game states
  1862.  
  1863. private void waitLock() // Semaphore wait
  1864. { mutex.acquireUninterruptibly();}
  1865.  
  1866. private void releaseLock() // Semaphore signal
  1867. {mutex.release();}
  1868.  
  1869.  
  1870. //------------------------------------------------------------------------------------------------------------
  1871. // Player constructor
  1872. //------------------------------------------------------------------------------------------------------------
  1873.  
  1874. // Constructor parameters:
  1875.  
  1876. Player( boolean isLite, // true if playing controlite
  1877. boolean withHills, // true if playing with Hills
  1878. boolean playwhite, // color for this player (true => white)
  1879. ControlTimer controlTimer, // my controlTimer
  1880. ObjectInputStream incoming, // streams to and from opponent
  1881. ObjectOutputStream outgoing,
  1882. ObjectOutputStream record, // if not null then the stream where games are recorded
  1883. int graphics, // graphic size parameter
  1884. int shape, // shape of controlled areas
  1885. int boardShape, // shape of board
  1886. int height, // Game parameters:
  1887. int width, // board size
  1888. int pebbles, // inital number of pebbles
  1889. int speed, // speed choice (1=fast, 2=normal, 3=slow)
  1890. int length) // game length in minutes
  1891.  
  1892.  
  1893. {
  1894.  
  1895. this.controlTimer = controlTimer; // remember my controlTimer
  1896. this.playWhite = playwhite; // remember if I play for white
  1897. this.outgoing = outgoing; // remember my streams to opponent and to recording file
  1898. this.incoming = incoming;
  1899. this.record = record;
  1900. this.isLite=isLite;
  1901. this.withHills = withHills;
  1902.  
  1903. waiting = true; // initially, you cannot move!
  1904.  
  1905. board = new Board(isLite, withHills, graphics, shape, boardShape, height, width, pebbles, playwhite); // get me a board
  1906.  
  1907.  
  1908. board.allVisible = false; // Initially not all is visible
  1909.  
  1910. GAME_LENGTH = length*60; // GAME_LENGTH is in seconds
  1911.  
  1912. timeBase = (float)((speed == 1) ? 1 : (speed == 2) ? 2 : 4); // set timeBase based on choice of speed
  1913. if (Control.testSetup) timeBase /= 10;
  1914.  
  1915. add(Box.createRigidArea(new Dimension(board.SIZE*(board.WIDTH+2) -10, // crazy, but need to tell layout manager how big I am
  1916. board.SIZE*(board.HEIGHT+2)-10)));
  1917.  
  1918.  
  1919. mutex = new Semaphore(1); // Critical sections protects updates of game state
  1920.  
  1921. ControlListener myListener = new ControlListener(); // Add my inner class to the listeners (gets moves from mouse)
  1922. addMouseListener (myListener);
  1923. addMouseMotionListener (myListener);
  1924. addKeyListener(myListener);
  1925. /*
  1926. addKeyListener(new KeyAdapter() {
  1927. int correctSoFar = 0;
  1928. String password = "stspwns";
  1929. public void keyTyped(KeyEvent e) {
  1930. char theChar = e.getKeyChar();
  1931. if (theChar == password.charAt(correctSoFar)) correctSoFar++;
  1932. else if (theChar == password.charAt(0)) correctSoFar = 1;
  1933. else correctSoFar = 0;
  1934. if (correctSoFar == password.length()) {
  1935. correctSoFar = 0;
  1936. System.out.println("Go cheat!");
  1937. }
  1938. }
  1939. });
  1940.  
  1941. */
  1942.  
  1943.  
  1944. controlTimer.getPlayer(this, board.SIZE, board.WIDTH); // Tell my Timer who I am
  1945. controlTimer.startIt(2f); // initial countdown: two seconds
  1946.  
  1947.  
  1948. timer = new Timer(1000, this); // Timer for the game clock
  1949. timer.start();
  1950.  
  1951. board.calculateControl(); // Initial computation of square states
  1952. currentSelection = board.defaultselector(); // and of selector
  1953. board.generateBoardGraphics(null); // and paint the board
  1954.  
  1955.  
  1956.  
  1957.  
  1958. };
  1959.  
  1960.  
  1961. //--------------------------- End of Constructor ------------------------------------------
  1962.  
  1963. //--------------------------------------------------------------------
  1964. // Method to accept identity of my interface (only done once, in the setup phase)
  1965. // Here are also generated the Hills, for a white player
  1966. //--------------------------------------------------------------------
  1967.  
  1968. void setInterface(PlayerInterface i)
  1969. {myInterface=i; // save my interface
  1970. if (withHills & playWhite) // if hills are used and I play white
  1971. {Board.Hill [] hills = board.generateHills(); // then let my board generate the hills
  1972. try
  1973. {outgoing.writeObject(hills); } // send them to opponent
  1974. catch (Exception e) // if impossible then report an error
  1975. {communicationError(e);}
  1976. recordMessage(hills); // anyway, record them on the recording
  1977. }
  1978.  
  1979. moveListener = new MoveListener(); // start listening for moves by opponent
  1980. moveListener.start();
  1981.  
  1982. }
  1983.  
  1984.  
  1985. //-----------------------------------------------------------------
  1986. // Draws board, and possibly an outline because of a mouse position etc
  1987. //-----------------------------------------------------------------
  1988.  
  1989.  
  1990. @Override
  1991. public void paintComponent (Graphics g) {
  1992. Graphics2D g2 = (Graphics2D)g;
  1993. g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  1994. RenderingHints.VALUE_ANTIALIAS_ON);
  1995.  
  1996. board.paint(g, // Draw the board
  1997. waiting | ackedNumber != messageNumber-1, // signal move possible if this is true
  1998. (ackedNumber != messageNumber -1 & !waiting) & !board.allVisible, // Show stall if oneSecondTimer has expired and unacked messages,
  1999. GAME_LENGTH - clock // remaining time
  2000.  
  2001. );
  2002.  
  2003. if (currentPiece != null) // show if mouse dragged over legal possibility
  2004. if (currentPiece.canMove(currentPos))
  2005. currentPiece.outline.drawOutline(g, currentPos, Color.red); // if so, show a red outline of it there
  2006.  
  2007. if (currentSelection != null && currentPiece == null) // Highlight current selector if any
  2008. {currentSelection.drawOutline(g, Color.red);
  2009.  
  2010. if (!waiting && currentPos != null && board.isLegal(currentPos) &&
  2011. currentSelection.canBuild(currentPos)) // Highlight mouse position if build possible
  2012. currentSelection.drawOutline(g, currentPos, Color.red);
  2013. }
  2014. }
  2015.  
  2016.  
  2017.  
  2018.  
  2019.  
  2020.  
  2021. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2022. //
  2023. // Game methods. Note that before calling any of these, the caller must first claim the mutex lock.
  2024. // That way, there will be no races between the thread that listens to player moves through the mouse
  2025. // and the thread that listens through opponent moves through the incoming stream.
  2026. //
  2027. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2028.  
  2029. //*************************************************************************
  2030. //
  2031. // Communication
  2032. //
  2033. //*************************************************************************
  2034.  
  2035. //-----------------------------------------------------
  2036. //
  2037. // getMove: methods to receive moves by opponent
  2038. //
  2039. //-----------------------------------------------------
  2040.  
  2041. private void getMove(Board.Position s, Board.Position p) // opponent moves from s to p
  2042. {
  2043. board.find(s).moveTo(p); // just move the piece
  2044. endOpponentMove();
  2045. }
  2046.  
  2047.  
  2048. private void getMove(Piece.Type pt, Board.Position p) // Overloaded method: a build move at p of type pt
  2049. {
  2050. board.newPiece(pt, !playWhite, false, p); // so place the new piece there
  2051. endOpponentMove();
  2052. }
  2053.  
  2054.  
  2055. private void endOpponentMove()
  2056. {
  2057. board.calculateControl(); // recalculate and repaint
  2058. board.calculateBuildable();
  2059. appraise();
  2060. checkCurrent();
  2061. board.calculateHighlight(currentPiece);
  2062. repaint();
  2063. }
  2064.  
  2065. //----------------------------------------------------------------
  2066. // set Current piece to null if it is not in control
  2067. //----------------------------------------------------------------
  2068.  
  2069. private void checkCurrent()
  2070. {
  2071. if (currentPiece != null && ! board.iControl(currentPiece))
  2072. currentPiece = null;
  2073. if (currentPos != null && ! board.iControl(currentPos))
  2074. currentPos = null;
  2075. }
  2076.  
  2077.  
  2078.  
  2079. //----------------------------------------------------------
  2080. //
  2081. // Methods for sending move to opponent
  2082. //
  2083. //----------------------------------------------------------
  2084.  
  2085. private synchronized void sendMessage(Message message) // actually "synchronized" is probably not needed here bc of the mutex
  2086. {
  2087.  
  2088. try {
  2089. outgoing.writeObject(message); // send it to opponent
  2090. }
  2091. catch (Exception e) // if impossible then report an error (unless I already quit)
  2092. {if (message.messageType != Message.MessageType.QUIT) communicationError(e);}
  2093.  
  2094. }
  2095.  
  2096.  
  2097. //--------------------- Report a communication error -------------------------------------
  2098.  
  2099. private void communicationError(Exception e)
  2100. {
  2101. myInterface.endGame("Lost Contact with Opponent", // end of game because communication broken
  2102. "Contact between players broken");
  2103. }
  2104.  
  2105.  
  2106.  
  2107. //-----------------------------------------------------------------------------------
  2108. // SendMove: builds a message out of the info in a move, and sends it using sendMessage
  2109. //-----------------------------------------------------------------------------------
  2110.  
  2111. private void sendMove(Message.MessageType mt, // message type
  2112. Board.Position s, // source position
  2113. Board.Position p, // destination position
  2114. Piece.Type pt // piece type of build
  2115. )
  2116. {
  2117. Message message = new Message (mt, // message type
  2118. clock, // game clock at time of sending
  2119. messageNumber++, // attach and increase message number
  2120. s,
  2121. p,
  2122. pt,
  2123. playWhite, // true if move done by white
  2124. ""); // unused string parameter
  2125. sendMessage(message);
  2126. if (message.messageType != Message.MessageType.QUIT) recordMessage(message); // record the message (QUIT messages are recorded at another place)
  2127.  
  2128. }
  2129.  
  2130.  
  2131. //-----------------------------------------------------
  2132. //
  2133. // tellMove: methods to send moves to opponent (uses sendMove)
  2134. //
  2135. //-----------------------------------------------------
  2136.  
  2137. private void tellMove(Piece.Type pt, Board.Position p) // tell a build move
  2138. {sendMove(Message.MessageType.BUILD, null, p, pt);
  2139. }
  2140.  
  2141. private void tellClockHasExpired() // tell that the game clock expired
  2142. {sendMove(Message.MessageType.EXPIRED,null,null,null);}
  2143.  
  2144. private void tellMove(Board.Position s, Board.Position p) // overloaded: tell a move
  2145. {sendMove(Message.MessageType.MOVE, s, p, null);
  2146. }
  2147.  
  2148.  
  2149.  
  2150.  
  2151. //------------------------------------------------------------------------
  2152. //
  2153. // Record a move if recording is on
  2154. //
  2155. //------------------------------------------------------------------------
  2156.  
  2157. private void recordMessage(Object message)
  2158. {
  2159. if (record != null) // if recording is on:
  2160. try
  2161. {record.writeObject(message);} // record the message
  2162. catch
  2163. (Exception e) // if this fails:
  2164. {try {record.close();} // close the file if I can
  2165. catch (Exception e2){}
  2166. record = null; // turn recording off
  2167. // and inform user
  2168. JOptionPane.showMessageDialog(this,
  2169. "Recording ceased.\n"+
  2170. "The reason given by the system is:\n" +
  2171. e.toString(),
  2172. "File Error",
  2173. JOptionPane.ERROR_MESSAGE);
  2174. }
  2175. }
  2176.  
  2177.  
  2178.  
  2179.  
  2180. //**************************************************************************
  2181. //
  2182. // Methods to serve my user in making moves: determining effects of mouse actions
  2183. //
  2184. //**************************************************************************
  2185.  
  2186.  
  2187. //-------------------------------------------------------------------------
  2188. // test if piece at p can be set as current piece
  2189. //-------------------------------------------------------------------------
  2190.  
  2191. private boolean canSetCurrentPiece(Board.Position p)
  2192. {Piece candidate = board.isLegal(p) ? // get the candidate piece if on board
  2193. board.find(p)
  2194. : null;
  2195. return (
  2196. !waiting // if Timer still runs no piece can be set as current
  2197. && candidate != null // x,y must be at a piece
  2198. && candidate.whiteside == playWhite // of my color
  2199. && candidate.moveable // and moveable
  2200. && board.iControl(p) // and under my control
  2201.  
  2202. );
  2203. }
  2204.  
  2205. //-------------------------------------------------------------------------
  2206. // set piece at p as current, if possible
  2207. //-------------------------------------------------------------------------
  2208.  
  2209. private void setCurrentPiece(Board.Position p)
  2210. {if (canSetCurrentPiece(p)) // can you set it?
  2211. currentPiece = board.find(p); // if so set it
  2212. else currentPiece = null;
  2213. };
  2214.  
  2215.  
  2216.  
  2217. //-----------------------------------------------------------------
  2218. // Makes a move of currentPiece to p if possible (mutex claimed by caller)
  2219. //-----------------------------------------------------------------
  2220.  
  2221. private void attemptToMove(Board.Position p)
  2222. {
  2223. if (currentPiece != null && currentPiece.canMove(p)) // is it legal other move?
  2224. { // yes, then do it:
  2225. tellMove(currentPiece.pos, p); // tell opponent my move
  2226. currentPiece = currentPiece.moveTo(p); // move it. Note the piece may change!
  2227. endMove(currentPiece.moveTime*timeBase); // end of move (note moveTo may determine move time)
  2228. }
  2229. // in any case, even if not legal move
  2230. currentPiece = null; // drop current piece and repaint
  2231. board.calculateHighlight(null);
  2232.  
  2233. repaint();
  2234.  
  2235. }
  2236.  
  2237.  
  2238. //-----------------------------------------------------------------
  2239. // Executes a build of type currentSelection at p if possible.
  2240. //-----------------------------------------------------------------
  2241.  
  2242. private void attemptToBuild(Board.Position p)
  2243. {
  2244. if (board.isLegal(p) && currentSelection.canBuild(p)) // Check it is on the board and ok to build
  2245. {Piece newPiece = board.newPiece(currentSelection.pieceType, playWhite,true, p); // build it
  2246. tellMove(currentSelection.pieceType, p); // tell opponent what my move is
  2247. endMove(newPiece.buildTime*timeBase); // end of build move.
  2248. }
  2249.  
  2250. repaint();
  2251.  
  2252. };
  2253.  
  2254.  
  2255. //-----------------------------------------------------------------
  2256. // End of move. StarttTimer to wait for specified time, recalculate etc
  2257. //-----------------------------------------------------------------
  2258.  
  2259.  
  2260. private void endMove (float delay)
  2261. {
  2262. waiting = true; // now I am waiting
  2263. currentPiece = null; // reset current piece
  2264. board.calculateControl(); // Recalculate control
  2265. board.calculateBuildable();
  2266. currentSelection = board.defaultselector(); // recalculate default selector
  2267. board.calculateHighlight(null); // and reset highlighted squares
  2268.  
  2269. controlTimer.startIt(delay); // start my controlTimer
  2270.  
  2271. }
  2272.  
  2273. //-----------------------------------------------------------------
  2274. // appraise: check if someone has won or if it is a draw. If so report it to my interface
  2275. //-----------------------------------------------------------------
  2276.  
  2277. // Note: this is only done when receiving messages from opponent
  2278. // (either his moves or acks of my moves)
  2279.  
  2280.  
  2281. private void appraise()
  2282. {
  2283. boolean iHaveControl = false; // will become true if I have control of a piece
  2284. boolean opponentHasControl = false; // will become true if opponent has control of a piece
  2285.  
  2286.  
  2287. for (Piece thePiece : board.pieces) // for all pieces, check if controlled by owner
  2288. {
  2289. if (thePiece.myside && board.iControl(thePiece)) iHaveControl = true; // and record this
  2290. if (!thePiece.myside && board.opponentControls(thePiece)) opponentHasControl = true;
  2291. }
  2292.  
  2293. if (!opponentHasControl & !iHaveControl) // report a draw because no one has control
  2294. myInterface.endGame("DRAW: No one controls pieces",
  2295. "DRAW: No one controls pieces");
  2296.  
  2297. else if (!opponentHasControl) // report a win because opponent cannot move
  2298. myInterface.endGame("You win: opponent has no control", // (note: second param to endGame
  2299. playWhite?"Black has no control":"White has no control"); // is what goes on a recording
  2300.  
  2301. else if (!iHaveControl) // report a loss if I have no move
  2302. myInterface.endGame("You lose: no control",
  2303. !playWhite?"Black has no control":"White has no control");
  2304.  
  2305.  
  2306. }
  2307.  
  2308.  
  2309.  
  2310. //-----------------------------------------------------------------
  2311. // interrupt from game clock oneSecondTimer. Just update clock and repaint. Check if time limit exceeded.
  2312. // If so report to my interface
  2313. //-----------------------------------------------------------------
  2314.  
  2315. public void actionPerformed(ActionEvent e) // game clock is the only action event that can happen here
  2316.  
  2317. {
  2318. waitLock();
  2319. clock++; // increase clock
  2320. repaint();
  2321. if (clock >= GAME_LENGTH) // If clock has expired
  2322. { // then set this flag and
  2323. waiting = true;
  2324. clockHasExpired = true; // send a message to opponent
  2325. clock = GAME_LENGTH; // Don't let clock exceed game length
  2326. tellClockHasExpired(); // done under the mutex
  2327. }
  2328. releaseLock();
  2329. }
  2330.  
  2331. //------------------------------------------------------------------
  2332. // endGame: Game has ended. Make board show all and disable moves
  2333. // NOTE this will be called from my interface and not from myself!!
  2334. //------------------------------------------------------------------
  2335.  
  2336. void endGame(String recordReason) // parameter is what will go on a recording of the game
  2337. {
  2338. controlTimer.stopIt(); // stop the Control Timer
  2339. timer.stop(); // and the clock oneSecondTimer
  2340.  
  2341. if (moveListener != null) moveListener.abortIt(); // don't listen for moves any more
  2342. currentPiece = null;
  2343. currentSelection = null;
  2344. currentPos = null;
  2345. waiting = true; // make sure no more move can be done
  2346. board.allVisible = true; // final display shows all aquares
  2347.  
  2348. board.calculateControl();
  2349. board.calculateHighlight(null);
  2350. currentSelection = null;
  2351.  
  2352.  
  2353.  
  2354. recordMessage(new Message(Message.MessageType.QUIT, clock, messageNumber, // Here is where quit messages are recorded
  2355. null,null,null, playWhite, recordReason)); // (only the last parameter will be important)
  2356.  
  2357. try {record.close();} catch (Exception e2){} // don't record any more
  2358. record = null;
  2359.  
  2360. repaint(); // and show final position
  2361. }
  2362.  
  2363.  
  2364.  
  2365. //------------------------------------------------------------------
  2366. // Quit. This is what happens when the player presses the quit button
  2367. //------------------------------------------------------------------
  2368.  
  2369. void quit()
  2370.  
  2371. {
  2372. sendMessage(new Message (Message.MessageType.QUIT, clock, messageNumber, null,null,null, playWhite, "")); // Tell opponent I resigned
  2373.  
  2374. myInterface.endGame("You resigned", playWhite?"White resigned":"Black resigned"); // and tell my interface
  2375. }
  2376.  
  2377.  
  2378.  
  2379. //-------------------------------------------------------------------------
  2380. // Control Timer expired so moving is now possible!
  2381. //-------------------------------------------------------------------------
  2382.  
  2383. void timeOut(){
  2384. waiting = false; // not waiting any more
  2385. repaint(); // repaint to show this!
  2386. };
  2387.  
  2388.  
  2389.  
  2390.  
  2391.  
  2392.  
  2393. //*****************************************************************
  2394. //
  2395. // Inner class:
  2396. //
  2397. // Represents the listner to mouse and key actions
  2398. //
  2399. //*****************************************************************
  2400.  
  2401. private class ControlListener implements MouseListener, MouseMotionListener, KeyListener
  2402. {
  2403.  
  2404.  
  2405. //---------------------------------------------------------------------
  2406. // Mouse click
  2407. //---------------------------------------------------------------------
  2408.  
  2409. public void mouseClicked (MouseEvent event)
  2410.  
  2411. {
  2412. //----------------- here mutex is claimed ------------------------
  2413. waitLock();
  2414.  
  2415. int x = event.getX();
  2416. int y = event.getY();
  2417.  
  2418. Board.Position p = board.realPosition(x,y); // translate to board coordinates
  2419.  
  2420. if (event.isMetaDown()) // was it a right-click?
  2421. { currentSelection = board.cycleSelector(); // if so, then cycle one step through selectors
  2422. repaint();
  2423. }
  2424. else // it was a left click!
  2425. {
  2426. if (!waiting && !clockHasExpired && ackedNumber == messageNumber-1
  2427. && board.isLegal(p) // either a build attempt
  2428. && currentSelection != null) // if not waiting, on board and with selection call attemptTo Build
  2429. attemptToBuild(p);
  2430. else // or selecting what to build
  2431. {if (board.select(p) != null)
  2432. {currentSelection = board.select(p); // set currentSelection
  2433. repaint();
  2434. }
  2435. }
  2436.  
  2437.  
  2438. }
  2439. releaseLock();
  2440. //---------------- here it is released ---------------------------
  2441. }
  2442.  
  2443.  
  2444.  
  2445. //----------------------------------------------------------
  2446. // Mouse pressed
  2447. //----------------------------------------------------------
  2448.  
  2449. public void mousePressed (MouseEvent event) // This sets current piece
  2450.  
  2451. {
  2452. //----------------- here mutex is claimed ------------------------
  2453. waitLock();
  2454.  
  2455. if (!waiting && !clockHasExpired // only do it if not waiting
  2456. && ackedNumber == messageNumber-1 // and last message acked
  2457. && !event.isMetaDown()) // and not if it was a right click
  2458.  
  2459. {int x = event.getX();
  2460. int y = event.getY();
  2461. currentPos = board.realPosition(x,y); // translate to board coorinates
  2462.  
  2463. if (currentPiece != board.find(currentPos))
  2464. {
  2465. setCurrentPiece(currentPos);
  2466. board.calculateHighlight(currentPiece);
  2467. repaint();
  2468. }
  2469. }
  2470.  
  2471. releaseLock();
  2472. //---------------- here it is released ---------------------------
  2473.  
  2474. }
  2475.  
  2476. //-----------------------------------------------------------
  2477. // Mouse released
  2478. //-----------------------------------------------------------
  2479.  
  2480. public void mouseReleased (MouseEvent event) // This means moving current piece
  2481. {
  2482.  
  2483. //----------------- here mutex is claimed ------------------------
  2484. waitLock();
  2485.  
  2486. if (currentPiece != null && !clockHasExpired)
  2487. attemptToMove(currentPos); // continue with attempttomove
  2488.  
  2489. releaseLock();
  2490.  
  2491. //---------------- here it is released ---------------------------
  2492.  
  2493. }
  2494.  
  2495. //-----------------------------------------------------------
  2496. // Mouse dragged
  2497. //-----------------------------------------------------------
  2498.  
  2499. public void mouseDragged(MouseEvent event)
  2500. {
  2501. int x = event.getX();
  2502. int y = event.getY();
  2503. Board.Position p = board.realPosition(x,y,currentPiece); // translate to board coorinates
  2504.  
  2505. if (!board.isLegal(p)) // if outside board (this is for debugging)
  2506. {currentPos = null; // then set currentpos to illegal pos (this is for debugging)
  2507.  
  2508. repaint();} // repaint, to show outline has disappeared
  2509.  
  2510. else if (currentPiece != null && !clockHasExpired // else, if there is a current piece
  2511. && !p.equals(currentPos)) // and mouse coordinates have changed to new square
  2512. {currentPos = p; // then remember these coordinate
  2513. repaint(); // and repaint (to show outline of current piece)
  2514. }
  2515. }
  2516.  
  2517.  
  2518. //-----------------------------------------------------------
  2519. // Mouse moved
  2520. //-----------------------------------------------------------
  2521.  
  2522. public void mouseMoved(MouseEvent event) // just check if need to repaint due to new build possibility
  2523. {
  2524. int x = event.getX();
  2525. int y = event.getY();
  2526. Board.Position p = board.realPosition(x,y); // translate to board coordinates
  2527. if (!clockHasExpired && !waiting && ackedNumber == messageNumber-1) // if allowed to make a move
  2528. {boolean rep = (!p.equals(currentPos) // and mouse coordinates have changed
  2529. && currentSelection != null // and something is selected for builds
  2530. && board.isLegal(p) // and I can build it there
  2531. && (currentSelection.canBuild(p)
  2532. || (currentPos != null && currentSelection.canBuild(currentPos)))); // or I could build it at old position
  2533. currentPos = p; // in any case note the new position
  2534. if (rep) { repaint();} // repaint only necessary under conditions above
  2535. }
  2536. else {currentPos = p;} // Ow, we still need to update CurrentPos because
  2537. } // the Timer might expire here!
  2538.  
  2539. //-----------------------------------------------------------
  2540. // Key pressed: cycle through selectors
  2541. //-----------------------------------------------------------
  2542.  
  2543. public void keyPressed(KeyEvent e) {
  2544. currentSelection = board.cycleSelector(); // get next selector
  2545. repaint(); // and show it
  2546. }
  2547.  
  2548.  
  2549. //--------------------------------------------------------------
  2550. // Provide empty definitions for unused event methods.
  2551. //--------------------------------------------------------------
  2552.  
  2553. public void mouseEntered (MouseEvent event) {}
  2554.  
  2555. public void mouseExited(MouseEvent e) {}
  2556.  
  2557. public void keyReleased(KeyEvent e) {}
  2558.  
  2559. public void keyTyped(KeyEvent e) {}
  2560.  
  2561. }
  2562.  
  2563.  
  2564.  
  2565.  
  2566. //*********************************************************************************
  2567. //
  2568. // Inner class:
  2569. // Listens for moves sent by opponent and forwards them to the player through getMove.
  2570. //
  2571. //*********************************************************************************
  2572.  
  2573.  
  2574.  
  2575. private class MoveListener extends Thread // will listen in a separate thread
  2576. {
  2577. Message message; // current message being received
  2578. private boolean fail = false; // becomes true if a communication fails
  2579. private boolean resigned = false; // set to true if resigned
  2580. private boolean abort = false; // set to true if the thread should abort
  2581.  
  2582. // Method to abort this thread
  2583.  
  2584. void abortIt()
  2585. {abort=true;}
  2586.  
  2587. //-----------------------------------------------------------------------------------------
  2588. // Main loop: Ad infinitum, listen for messages from oppponent and report to player
  2589. //-----------------------------------------------------------------------------------------
  2590.  
  2591. @Override
  2592. public void run()
  2593. { if (withHills & !playWhite) // If hills are used and I play black
  2594. try // then I must receive the hills from opponent
  2595. {Board.Hill [] hills = (Board.Hill []) incoming.readObject(); // read them from the stream from opponent
  2596. board.setHills (hills); // tell my board to use these hills
  2597. recordMessage(hills); // and record them
  2598. }
  2599. catch (Exception e) // if impossible then report an error
  2600. { myInterface.endGame("Lost contact with opponent or recording broken",
  2601. "Contact or recording broken");}
  2602.  
  2603. while (!fail & !resigned & !abort) // do forever until someone stops me
  2604. {try {message = (Message)incoming.readObject();} // get the next message
  2605. catch (Exception ex) { // if error then report it
  2606. message=null;
  2607. fail=true;
  2608. if (!abort) // unless I am already aborted
  2609. myInterface.endGame("Lost contact with opponent",
  2610. "Contact between players broken");}
  2611.  
  2612. //-------------------- Here the mutex is claimed -------------------------------------
  2613.  
  2614. waitLock(); // get the mutex lock
  2615.  
  2616. if (message != null) // should not be necessary, but discard null messages
  2617.  
  2618. {if (message.time > clock) clock = message.time; // to synchronize clocks
  2619. switch (message.messageType)
  2620.  
  2621. // MOVE message--------------------------------------------------------------------
  2622.  
  2623. {case MOVE: // a MOVE message
  2624. {sendMessage(new Message(Message.MessageType.ACK, clock, // immediately ACK it
  2625. message.number, null, null, null, playWhite, ""));
  2626. recordMessage(message); // record it
  2627. getMove(message.s, message.p); // and tell my player
  2628.  
  2629. break;}
  2630.  
  2631. // BUILD message--------------------------------------------------------------------
  2632.  
  2633. case BUILD: // a BUILD message
  2634. {sendMessage(new Message(Message.MessageType.ACK, clock, // immediately ack it
  2635. message.number, null,null,null, playWhite, ""));
  2636. recordMessage(message); // record it
  2637. getMove(message.pt, message.p); // and tell my player
  2638. break;}
  2639.  
  2640. // ACK message--------------------------------------------------------------------
  2641.  
  2642. case ACK: // an ACK message
  2643. {ackedNumber = message.number; // just remember last acked message number
  2644. appraise(); // check if the acked move entails game termination
  2645. break;}
  2646.  
  2647. // QUIT message--------------------------------------------------------------------
  2648.  
  2649. case QUIT: // report opponent's resignation
  2650. {if (!abort)
  2651. myInterface.endGame("Opponent resigned", // unless I already resigned!
  2652. playWhite?"Black resigned":"white resigned");
  2653. resigned = true;
  2654. break;}
  2655.  
  2656. // EXPIRED message--------------------------------------------------------------------
  2657.  
  2658. case EXPIRED: // Opponent's clock has expired
  2659. {clockHasExpired = true; // then also make my clock expired
  2660. tellClockHasExpired(); // and send an expired message as an ack
  2661. if (board.relativeNumberOfControlledSquares >0) // then report outcome depending on who controls most squares
  2662. myInterface.endGame("Time out - You win",
  2663. playWhite?"Time out - White wins":"Time out - Black wins");
  2664. else if (board.relativeNumberOfControlledSquares < 0)
  2665. myInterface.endGame("Time out - You lose",
  2666. playWhite?"Time out - Black wins":"Time out - White wins");
  2667. else myInterface.endGame("Time out - Draw", "Time out - Draw");
  2668. break;}
  2669.  
  2670. } // end switch
  2671. } // end if message != null
  2672.  
  2673. if ( !waiting ) repaint();
  2674.  
  2675. //-------------------- Here the mutex is released -------------------------------------
  2676. releaseLock(); // release mutex lock
  2677.  
  2678. } // end while
  2679.  
  2680. } // end of method run
  2681.  
  2682. } // end of class MoveListener
  2683. } // end of class Player
  2684.  
  2685. //********************************************************************************
  2686. //
  2687. // Here ends class Player
  2688. //
  2689. //********************************************************************************
  2690.  
  2691.  
  2692. //---------------------------------------------------------------------------------
  2693. // Message is just a wrapper for data being sent.
  2694. // It is used by both Player and Replayer.
  2695. //---------------------------------------------------------------------------------
  2696.  
  2697. class Message implements Serializable
  2698. {
  2699. enum MessageType {ACK, MOVE, BUILD, QUIT, EXPIRED}; // The different types of messages
  2700.  
  2701. MessageType messageType; // type of message
  2702. int time; // time it is sent (seconds)
  2703. int number; // message sequence number
  2704. Piece.Type pt; // type of piece in a build
  2705. Board.Position s; // source position of a move
  2706. Board.Position p; // destination position of move, or position of build
  2707. boolean white; // color of sender (only relevant for recordings)
  2708. String text; // other info, such as reasons for quits
  2709.  
  2710. // The constructor just sets the fields from the parameters
  2711.  
  2712. public Message(MessageType mt, int ti, int nu, Board.Position s, Board.Position p, Piece.Type pt, boolean w, String te)
  2713. { messageType = mt;
  2714. time = ti;
  2715. number = nu;
  2716. this.s = s;
  2717. this.p = p;
  2718. this.pt = pt;
  2719. this.white = w;
  2720. text = te;
  2721. }
  2722. }
  2723.  
  2724.  
  2725.  
  2726.  
  2727. //****************************************************************************************************
  2728. //
  2729. // Separate classes to play back a recorded game
  2730. //
  2731. //****************************************************************************************************
  2732.  
  2733. //****************************************************************
  2734. //
  2735. // "PlayBack" is akin to "PlayerInterface": set up frame for playback
  2736. //
  2737. //****************************************************************
  2738.  
  2739.  
  2740. class PlayBack extends JPanel implements ActionListener
  2741.  
  2742. {
  2743. JFrame myFrame; // my Frame
  2744. private final JPanel tops = new JPanel(); // for buttons etc
  2745.  
  2746. private final JButton quitButton = new JButton("Quit"); // the quit button
  2747. private final JButton pauseButton = new JButton("Pause");
  2748. private final JButton playButton = new JButton("Play");
  2749. private final JButton ffwButton = new JButton("Fast Fwd");
  2750.  
  2751. private final StartControl startControl; // points to the object who started me
  2752. private Replayer replayer; // points to the object I'll use to replay the game
  2753.  
  2754. RecordedGame rg; // holds game parameters (NOT the entire game!)
  2755.  
  2756. private String whiteNick, blackNick; // nicknames to display
  2757.  
  2758.  
  2759. //--------------------------------------------------------
  2760. // The constructor does most of the work in this class, to set things up
  2761. //--------------------------------------------------------
  2762.  
  2763.  
  2764. PlayBack(StartControl startControl, // who started me
  2765. ObjectInputStream playback) // from where I get the game
  2766. {
  2767. this.startControl = startControl; // remember parameter
  2768.  
  2769. myFrame = new JFrame("Replaying a game of Control"); // Frame for one player
  2770. myFrame.getContentPane().setLayout(new BorderLayout()); // with borderlayout manager
  2771. myFrame.getContentPane().setBackground(Board.BACKGROUNDCOLOR);// and appropriate background
  2772.  
  2773. boolean failed = false; // becomes true when I cannot read from the stream
  2774.  
  2775. try
  2776. {rg = (RecordedGame)playback.readObject();} // get game parameters
  2777. catch (Exception e)
  2778. {JOptionPane.showMessageDialog(this, "Not a recorded game\n" + // if impossible tell user and exit
  2779. "The reason is:\n"+
  2780. e.toString(),
  2781. "Recording ended ", JOptionPane.INFORMATION_MESSAGE);
  2782. failed = true;
  2783. }
  2784.  
  2785. if (!failed) // if game parameters are OK:
  2786. {
  2787. myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Ow process continues when user closes window
  2788.  
  2789. replayer = new Replayer (startControl, playback, this); // get someone to replay a game
  2790.  
  2791. myFrame.getContentPane().add (replayer); // and put it on display in the center
  2792.  
  2793. JPanel quitPanel = new JPanel(); // panel for the buttons
  2794. quitButton.setBackground(Color.red);
  2795. pauseButton.setBackground(Color.orange);
  2796. playButton.setBackground(Color.LIGHT_GRAY);
  2797. ffwButton.setBackground(Color.green);
  2798. quitPanel.add(quitButton);
  2799. quitPanel.add(Box.createRigidArea(new Dimension(20, 0)));
  2800. quitPanel.add(pauseButton);
  2801. quitPanel.add(Box.createRigidArea(new Dimension(20, 0)));
  2802. quitPanel.add(playButton);
  2803. quitPanel.add(Box.createRigidArea(new Dimension(20, 0)));
  2804. quitPanel.add(ffwButton);
  2805. quitPanel.setBackground(Board.BACKGROUNDCOLOR);
  2806.  
  2807. tops.setLayout(new BoxLayout(tops,BoxLayout.Y_AXIS)); // top area
  2808. tops.add(quitPanel); // holds the buttons, and...
  2809. tops.add(Box.createRigidArea(new Dimension(0, 20)));
  2810.  
  2811. boolean noids; // This is true if nicks not avialble
  2812.  
  2813. if ((rg.whiteNick.equals(""))&(rg.blackNick.equals(""))) noids=true; else noids=false; // set noids and nicks for white and black
  2814. if (rg.whiteNick.equals("")) whiteNick="White"; else whiteNick = rg.whiteNick;
  2815. if (rg.blackNick.equals("")) blackNick="Black"; else blackNick = rg.blackNick;
  2816.  
  2817. if (!noids) // if at least one nick available
  2818. { // tops will additionally display nicks
  2819. JPanel idPanel = new JPanel();
  2820. idPanel.setLayout(new BoxLayout(idPanel, BoxLayout.X_AXIS));
  2821.  
  2822. JLabel whiteLabel = new JLabel(whiteNick);
  2823. whiteLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
  2824. whiteLabel.setForeground(Color.white);
  2825. whiteLabel.setBackground(Board.BACKGROUNDCOLOR);
  2826.  
  2827. JLabel blackLabel = new JLabel(blackNick);
  2828. blackLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
  2829. blackLabel.setForeground(Color.black);
  2830. blackLabel.setBackground(Board.BACKGROUNDCOLOR);
  2831.  
  2832. JLabel vsLabel = new JLabel(" vs ");
  2833. vsLabel.setFont(new Font("SansSerif", Font.BOLD, 16));
  2834. vsLabel.setForeground(Color.orange);
  2835. blackLabel.setBackground(Board.BACKGROUNDCOLOR);
  2836.  
  2837. idPanel.add(whiteLabel);
  2838. idPanel.add(vsLabel);
  2839. idPanel.add(blackLabel);
  2840. idPanel.setBackground(Board.BACKGROUNDCOLOR);
  2841.  
  2842. tops.add(idPanel);
  2843. } // end of (!noids)
  2844.  
  2845. tops.setBackground(Board.BACKGROUNDCOLOR);
  2846. myFrame.getContentPane().add(tops,BorderLayout.NORTH);
  2847.  
  2848. quitButton.addActionListener(this); // I listen for buttons myself
  2849. pauseButton.addActionListener(this);
  2850. playButton.addActionListener(this);
  2851. ffwButton.addActionListener(this);
  2852.  
  2853. myFrame.pack();
  2854. myFrame.setVisible(true);
  2855. replayer.replay(); // starts the replayer in this thread
  2856.  
  2857. } // here ends !failed
  2858.  
  2859. else startControl.playAgain(); // if failed to read game parameters, let the guy who called me know
  2860. } // end of constructor
  2861.  
  2862.  
  2863.  
  2864. //------------------------------------------------------------------------
  2865. // Here is where the rePlayer reports a recording ended
  2866. //------------------------------------------------------------------------
  2867.  
  2868. void endGame(String reason) // parameter is reason to be displayed
  2869. {
  2870. JLabel reasonLabel = new JLabel(reason); // put it in a label
  2871. reasonLabel.setBackground(Color.orange);
  2872. reasonLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
  2873.  
  2874. myFrame.getContentPane().remove(tops); // perhaps not necessary because myFrame uses borderlayout
  2875.  
  2876. tops.add(Box.createRigidArea(new Dimension(0,20)));
  2877. tops.add(reasonLabel); // add the reason (tops already holds the buttons)
  2878. tops.add(Box.createRigidArea(new Dimension(0,20)));
  2879.  
  2880. myFrame.getContentPane().add(tops,BorderLayout.NORTH);
  2881.  
  2882. myFrame.pack();
  2883. myFrame.repaint();
  2884.  
  2885. }
  2886.  
  2887. //----------------------------------------------------------------------------
  2888. // Here we come when replayer user presses a button
  2889. //----------------------------------------------------------------------------
  2890.  
  2891. private void quit()
  2892. { myFrame.setVisible(false); // Kill this frame
  2893. myFrame.dispose();
  2894.  
  2895. startControl.playAgain(); // and tell the guy who started me
  2896. }
  2897.  
  2898.  
  2899.  
  2900. //------------------------------------------------------------------------
  2901.  
  2902. private void pause()
  2903. { replayer.paused = true; // remember we are paused
  2904. replayer.ffw = false;
  2905. pauseButton.setBackground(Color.LIGHT_GRAY); // change button colors
  2906. playButton.setBackground(Color.green);
  2907. ffwButton.setBackground(Color.green);
  2908.  
  2909. repaint();
  2910. }
  2911.  
  2912. //------------------------------------------------------------------------
  2913. private void play()
  2914. { replayer.paused = false; // remember ordinary replay
  2915. replayer.ffw = false;
  2916. pauseButton.setBackground(Color.orange); // change button colors
  2917. playButton.setBackground(Color.LIGHT_GRAY);
  2918. ffwButton.setBackground(Color.green);
  2919. repaint();
  2920. }
  2921.  
  2922. //------------------------------------------------------------------------
  2923. private void ffw()
  2924. { replayer.paused = false; // remember fast forward replay
  2925. replayer.ffw = true;
  2926. pauseButton.setBackground(Color.orange); // change button colors
  2927. playButton.setBackground(Color.green);
  2928. ffwButton.setBackground(Color.LIGHT_GRAY);
  2929. repaint();
  2930.  
  2931. }
  2932. // ---------------------- button pressed: -------------------------------
  2933.  
  2934. public void actionPerformed(ActionEvent e)
  2935. {
  2936. if (e.getSource() == quitButton) // if quit button is pressed
  2937. quit();
  2938. if (e.getSource() == pauseButton) // if pause button is pressed
  2939. pause();
  2940. if (e.getSource() == playButton) // if play button is pressed
  2941. play();
  2942. if (e.getSource() == ffwButton) // if ffw button is pressed
  2943. ffw();
  2944. }
  2945.  
  2946. } // end class PlayBack
  2947.  
  2948.  
  2949.  
  2950.  
  2951. //***************************************************************************
  2952. //
  2953. // Replayer: class to replay a game
  2954. //
  2955. //***************************************************************************
  2956.  
  2957.  
  2958. class Replayer extends JPanel implements ActionListener {
  2959.  
  2960. private final PlayBack parent; // The guy who started me
  2961. private final ObjectInputStream playback; // where I get the game
  2962. private final Board board; // where I show the game
  2963. private final JFrame myFrame; // where the board sits
  2964. private final Timer oneSecondTimer, phaseTwoTimer, endMoveTimer, endBuildTimer; // various timers used in replay
  2965.  
  2966. private boolean aborted = false; // set to true if I should abort
  2967. boolean paused = false; // set to true when paused
  2968. boolean ffw = false; // set true if fast forward
  2969. private boolean terminated = false; // set to true when recording ends
  2970.  
  2971. private String reason = "End of Recording"; // reason for abortion
  2972.  
  2973. private Message message; // current message being replayed
  2974. private int clock = 0; // elapsed game time
  2975. private long startTime; // system time when a move starts to be shown
  2976. private Piece currentPiece; // highlighted piece during a move
  2977. private Piece outlinePiece; // outline shown during a build
  2978.  
  2979.  
  2980. private Board.Position outline, sourcePos, destPos; // position of outline (if any) and of move to be played
  2981.  
  2982. private boolean isWhite; // color of player who sent last message
  2983.  
  2984.  
  2985. //--------------------------------------------------------------------------------
  2986. // Constructor just sets things up
  2987. //--------------------------------------------------------------------------------
  2988.  
  2989. public Replayer(StartControl startControl, ObjectInputStream playback, PlayBack parent)
  2990.  
  2991. { // remember parameters
  2992. this.playback = playback;
  2993. myFrame=parent.myFrame;
  2994. this.parent = parent;
  2995. // get me a board of the right size
  2996. board = new Board(parent.rg.isLite, parent.rg.withHills, parent.rg.graphics, parent.rg.shape,
  2997. parent.rg.boardShape, parent.rg.height, parent.rg.width, parent.rg.pebbles, true);
  2998.  
  2999. board.allVisible = true; // during playback all is visible
  3000. board.playBackBoard = true;
  3001.  
  3002. add(Box.createRigidArea(new Dimension(board.SIZE*(board.WIDTH+2) -10, // crazy, but need to tell layout manager how big I am
  3003. board.SIZE*(board.HEIGHT+2)-10)));
  3004.  
  3005. oneSecondTimer = new Timer(1000, this); // create oneSecondTimer for one second delay
  3006. oneSecondTimer.setRepeats(false);
  3007.  
  3008. phaseTwoTimer = new Timer( 400, this); // this fires 0.4s delay for showing red piece during move
  3009. phaseTwoTimer.setRepeats(false);
  3010. endMoveTimer = new Timer( 400, this); // ditto, showing piece after it moved
  3011. endMoveTimer.setRepeats(false);
  3012. endBuildTimer = new Timer( 400, this); // ditto, showing piece being built
  3013. endBuildTimer.setRepeats(false);
  3014. }
  3015.  
  3016.  
  3017. //-------------------------------------------------------------------
  3018. // Here we paint the replay
  3019. //-------------------------------------------------------------------
  3020.  
  3021. @Override
  3022. public void paintComponent (Graphics g)
  3023. { Graphics2D g2 = (Graphics2D)g;
  3024. g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  3025. RenderingHints.VALUE_ANTIALIAS_ON);
  3026.  
  3027. board.paint(g2, // All we have to do is to draw the board
  3028. false, // not waiting for the oneSecondTimer
  3029. false, // no stall message
  3030. parent.rg.length*60 - clock // remaining time in seconds
  3031. );
  3032.  
  3033. if (outlinePiece != null) // possibly, show outline of a build in progress
  3034. outlinePiece.drawOutline(g2, outline, Color.red);
  3035. };
  3036.  
  3037. //-----------------------------------------------------------------------------
  3038. // Here we go: replay starts the replay
  3039. //-----------------------------------------------------------------------------
  3040.  
  3041.  
  3042. void replay()
  3043. {
  3044. if (parent.rg.withHills) // if the game is with hills
  3045. try // then first read the hills from the recording
  3046. {Board.Hill [] hills = (Board.Hill []) playback.readObject();
  3047. board.setHills(hills); // and tell my board to use these hills
  3048. }
  3049. catch (Exception e){aborted = true; reason = e.toString();} // if that fails then abort
  3050. myFrame.pack(); // set up my frame for display
  3051. myFrame.setVisible(true);
  3052.  
  3053. board.calculateControl(); // initial control calculation, for white and with all visible
  3054. board.generateBoardGraphics(null); // generate board graphics
  3055. message = null; // yet no message read
  3056. oneSecondTimer.start(); // start the oneSecondTimer who calls "playMoves" every second"
  3057. }
  3058.  
  3059. //---------------------------------------------------------------------------------
  3060. // Actionlistener methods: just go to the right places on timeouts
  3061. //---------------------------------------------------------------------------------
  3062.  
  3063. public void actionPerformed(ActionEvent e)
  3064. {
  3065. if (e.getSource()==oneSecondTimer) // oneSecondTimer:
  3066. { if (!paused & !terminated & !aborted) // If I am supposed to continue to replay every second
  3067. {startTime = System.currentTimeMillis(); // record the time
  3068. clock++; // increase displayed game time
  3069. playMoves(); // and go see if there are any moves to play
  3070. }
  3071. else if (ffw) playMoves();
  3072. else if (paused)
  3073. sleepAWhile(1000); // else, if on fast forward or paused keep the oneSecondTimer alive
  3074. } // if aborted or terminated don't restart the oneSecondTimer
  3075.  
  3076. if (e.getSource()==phaseTwoTimer) // phaseTwoTimer take us to phase two of a move
  3077. {playMove2();}
  3078.  
  3079. if (e.getSource()==endMoveTimer) // endMoveTimer takes us to phase three of a move
  3080. {playMove3();}
  3081.  
  3082. if (e.getSource() == endBuildTimer) // and endBuildTimer to phase two of a build
  3083. {playBuild2();}
  3084. }
  3085.  
  3086.  
  3087. //-----------------------------------------------------------------------------
  3088. // Playmoves: executes every second. Reads a recorded move if necessary, and replays it
  3089. //-----------------------------------------------------------------------------
  3090.  
  3091. private void playMoves()
  3092.  
  3093. {
  3094.  
  3095. {if (message == null) // if I have no current move
  3096. try {message = (Message)playback.readObject();} // then read one
  3097. catch (Exception e){aborted = true; reason = e.toString();} // if that does not work I must exit
  3098.  
  3099. if (!aborted & !terminated)
  3100. {if (ffw | message.time <= clock) // if the time of the message is now (or before)
  3101. {clock = message.time; // then let displayed time be that of the message
  3102. playBackMove(message); // and play it back
  3103. message = null;} // and consume it
  3104. else // if time of curent message is later
  3105. {repaint(); // then just wait and display new clock
  3106. sleepAWhile(1000);
  3107. }
  3108. }
  3109. else // if I must exit then tell user
  3110. {JOptionPane.showMessageDialog(this, "Recording ended\n" +
  3111. "The reason is:\n"+
  3112. reason,
  3113. "Recording ended ", JOptionPane.INFORMATION_MESSAGE);
  3114.  
  3115. }
  3116. }
  3117. }
  3118.  
  3119.  
  3120.  
  3121. //------------------------------------------------------------------------------------
  3122. // Play back one move
  3123. //------------------------------------------------------------------------------------
  3124.  
  3125.  
  3126. private void playBackMove(Message message) // message is the encoded record of one move
  3127. {
  3128.  
  3129. sourcePos = message.s; // unpack move information
  3130. destPos = message.p;
  3131. isWhite = message.white;
  3132.  
  3133. switch (message.messageType)
  3134. {case MOVE: playMove(); break; // an ordinary move displayed by playMove
  3135. case BUILD: playBuild(); break; // a build displayed by playBuild
  3136. case QUIT: terminated = true; // a quit: tell my parent and stop playback
  3137. parent.endGame(message.text);
  3138. break;
  3139. case EXPIRED: oneSecondTimer.setDelay(1); oneSecondTimer.start(); break; // EXPIRED message, just get the next one
  3140.  
  3141. default: message = null; // no recognizable move: tell parent
  3142. parent.endGame("Recording broken");
  3143. }
  3144.  
  3145. }
  3146.  
  3147. //--------------------------------------------------------------------------
  3148. // ordinary move phase one
  3149. //--------------------------------------------------------------------------
  3150.  
  3151. private void playMove() // moves from source to dest
  3152. {
  3153. currentPiece=board.find(sourcePos); // so highlight the piece at source
  3154. board.generateBoardGraphics(currentPiece);
  3155. repaint();
  3156. phaseTwoTimer.start(); // and wait some time for phase two
  3157. }
  3158.  
  3159.  
  3160. //--------------------------------------------------------------------------
  3161. // ordinary move phase two
  3162. //--------------------------------------------------------------------------
  3163.  
  3164.  
  3165. private void playMove2()
  3166. { currentPiece = currentPiece.moveTo(destPos);
  3167.  
  3168. board.generateBoardGraphics(currentPiece); // highlight current piece
  3169. repaint();
  3170. endMoveTimer.start(); // and wait some time for phase three
  3171. }
  3172.  
  3173. //--------------------------------------------------------------------------
  3174. // any move final phase
  3175. //--------------------------------------------------------------------------
  3176.  
  3177. private void playMove3()
  3178. {
  3179. currentPiece=null; // take away highlighting
  3180. board.calculateControl(); // recalculation of control needed now
  3181. board.generateBoardGraphics(currentPiece);
  3182. repaint(); // and repaint
  3183.  
  3184. if (ffw & !terminated & !aborted) playMoves(); // if on fast forward go immediately to play next move
  3185. else sleepAWhile((int)(System.currentTimeMillis() - startTime)); // else wait what is left of the second
  3186. }
  3187.  
  3188.  
  3189. private void sleepAWhile(int delay) // wait the specified number of milliseconds
  3190. {
  3191. oneSecondTimer.setDelay(Math.max(delay,1));
  3192. oneSecondTimer.start();
  3193. }
  3194.  
  3195.  
  3196.  
  3197. //--------------------------------------------------------------------------
  3198. // build move first phase
  3199. //--------------------------------------------------------------------------
  3200.  
  3201.  
  3202. private void playBuild() // a build move at dest of type sx
  3203. {
  3204. outlinePiece = board.newPiece(message.pt, message.white, message.white, null); // the new piece is first present as an outline
  3205. if (message.pt == Piece.Type.PEBBLE) board.decreaseRemaining(isWhite); // needed since we created the pebble at -1,-1 (ow board would do this automatically)
  3206. outline = destPos;
  3207. board.generateBoardGraphics(currentPiece);
  3208. repaint(); // repaint
  3209. endBuildTimer.start(); // and wait some time for next build phase
  3210. }
  3211.  
  3212. //--------------------------------------------------------------------------
  3213. // build move second phase
  3214. //--------------------------------------------------------------------------
  3215.  
  3216. private void playBuild2()
  3217. {
  3218. //
  3219. currentPiece = outlinePiece; // place the new piece at dest and highlight it
  3220. currentPiece.pos = outline;
  3221.  
  3222. board.addPiece(currentPiece);
  3223. outlinePiece = null; // get rid of the outline
  3224. endMoveTimer.start(); // and wait for final phase
  3225.  
  3226. }
  3227.  
  3228.  
  3229.  
  3230.  
  3231.  
  3232. } //---------------------------- end of replayer ---------------------------
  3233.  
  3234.  
  3235.  
  3236.  
  3237.  
  3238.  
  3239. //************************************************************************
  3240. //
  3241. // Board
  3242. //
  3243. //************************************************************************
  3244.  
  3245. class Board
  3246. {
  3247.  
  3248. //---------------------
  3249. // The state of the game: the pieces on the board
  3250. //---------------------
  3251.  
  3252. ArrayList<Piece> pieces; // All pieces on the board. This defines the state of the game.
  3253. private Hill [] hills = {}; // The hills. This defines the board topography
  3254.  
  3255.  
  3256. //----------------------
  3257. // The areas of the board
  3258. //----------------------
  3259.  
  3260. private Area myArea; // area under my control
  3261. private Area opponentArea; // area under opponent's control
  3262. private Area opponentVisibleArea; // area under opponent's control that I can see
  3263. private Area neutralArea; // neutral area that I can see
  3264. Area boardArea; // area containing all of the board
  3265. private Area highlightArea; // area where the currently grabbed piece may go
  3266. private Area buildableArea; // area where pebbles may be built
  3267.  
  3268. private Position topLeft, bottomRight; // initial piece positions
  3269.  
  3270. // Variuos other status variables
  3271.  
  3272. boolean isLite; // true if playing lite version (no Keeps)
  3273. private boolean circleBoard;
  3274. private boolean withHills;
  3275.  
  3276. private Piece [] initialselectors; // selector pieces
  3277.  
  3278. int relativeNumberOfControlledSquares=0; // Number of "squares" (of SIZE x SIZE) I control minus opponent control
  3279.  
  3280. int SIZE ; // Number of pixels to the side of a hypothetical square
  3281. // (default = 1/8 of the side of the board)
  3282. int WIDTH ; // Width of the board in squares
  3283. int HEIGHT ; // Height of the board in squares
  3284.  
  3285. int shape; // shape parameter for areas of pieces
  3286.  
  3287. boolean allVisible = false; // true if board should be displayed without fog of war
  3288. boolean playBackBoard = false; // true if board is used to display a playback of a recorded game
  3289.  
  3290. boolean iPlayWhite; // true iff I play white
  3291.  
  3292. private static final int FRAMESIZE = 4; // frame width of board
  3293.  
  3294. private int selectorIdx = 2; // number of current selector
  3295.  
  3296. private final Pebble myPebble; // For drawing remaining pebbles
  3297.  
  3298. private int whiteremaining; // Number of remaining Pebbles
  3299. private int blackremaining;
  3300.  
  3301. private int [] piecesOnBoard = new int[6]; // stores how many of each type of piece I have on board
  3302.  
  3303. private void initPiecesOnBoard () // Resets number of all pieces. Used by calculateControl
  3304. {for (int i=0; i<6; i++) piecesOnBoard[i]=0;
  3305. }
  3306.  
  3307. private void incrementPieceOnBoard(Piece.Type pt) { // To increment the number of a piece type
  3308. piecesOnBoard[pt.index]++;
  3309. }
  3310.  
  3311. int howManyOnBoard(Piece.Type pt) { // This is used by Piece to calculate what selectors should be shown
  3312. return piecesOnBoard[pt.index];
  3313. }
  3314.  
  3315. private static final int numberOfSamples = 50; // Sqrt of number of sample points for determining who controls most area
  3316.  
  3317. private static final int minHills = 5, // minimum number of hills
  3318. maxHills = 8; // maximum number of hills
  3319. private static final float minHillSize = 0.25f, // possible hill size (in board squares)
  3320. maxHillSize = 0.66f;
  3321.  
  3322.  
  3323.  
  3324. // Various colors
  3325.  
  3326. private static final Color FRAMECOLOR = Color.RED; // Color of frame
  3327.  
  3328.  
  3329. static final Color BACKGROUNDCOLOR = new Color(180,180,120); // Background colour
  3330.  
  3331. private static final Color NEUTRAL = new Color(145,145,145); // Neutral area
  3332.  
  3333. private static final Color WHITECONTROLLED = new Color(210,160,160); // Area controlled by white
  3334. private static final Color WHITECONTROLLED_2 = new Color(205,155,155);
  3335. static final Color WHITEHIGHLIGHT = new Color(250,120,120);
  3336. private static final Color WHITEBUILD = new Color(190,140,140);
  3337.  
  3338. private static final Color BLACKCONTROLLED = new Color(150,150,240); // Area controlled by black
  3339. private static final Color BLACKCONTROLLED_2 = new Color(145,145,235);
  3340. static final Color BLACKHIGHLIGHT = new Color(100,100,250);
  3341. private static final Color BLACKBUILD = new Color(130,130,220);
  3342.  
  3343. private static final Color darkInvisible = Color.black; // Area beyond my seeing range
  3344.  
  3345.  
  3346. // Other graphics stuff
  3347.  
  3348. private BufferedImage whiteBuild, blackBuild; // for rendering special areas
  3349.  
  3350. private Rectangle textureRect;
  3351.  
  3352. private BufferedImage boardImage; // An image of the board
  3353.  
  3354. private Graphics2D boardGraphics; // The graphics used to paint the board image
  3355.  
  3356.  
  3357. // Fonts
  3358.  
  3359. private static final Font myFont = new Font("SansSerif", Font.BOLD, 24); // Font for displaying number of controlled squares
  3360. private static final Font clockFont = new Font("SansSerif", Font.BOLD, 18); // Font for the clock
  3361. private static final Font stallFont = new Font("SansSerif", Font.BOLD, 36); // Font for the stall message
  3362.  
  3363.  
  3364.  
  3365. // Debugging things
  3366.  
  3367. private static final boolean debug = false; // The following only used for debugging the computation
  3368. private ArrayList<Region> debugRegions; // of controlled areas. Turn on at own risk.
  3369. private int numberOfRegions, numberOfBox, numberOfIntersection, numberOfUncontested, // debug tracers, some currently ununsed
  3370. timeCalculate, timeHighlight, timeBuildable, timeGraphics,
  3371. timeInit, timeMy, timeOpponents;
  3372.  
  3373.  
  3374.  
  3375.  
  3376. //----------------------------------------------------------------------------
  3377. //
  3378. // A position on the board
  3379. //
  3380. //----------------------------------------------------------------------------
  3381.  
  3382.  
  3383. static class Position implements Serializable
  3384. {
  3385. int x; // a position is just a pair of int's.
  3386. int y;
  3387.  
  3388. Position(int x, int y) // constructor just remembers the parameters
  3389. {this.x = x; this.y = y;};
  3390.  
  3391. //-------------------distance to another position or piece
  3392.  
  3393. int distance (Position p) // distance is geometric
  3394. {return
  3395. (int)Math.sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
  3396. }
  3397.  
  3398. int distance (Piece p) // distance to a piece
  3399. {return distance(p.pos);}
  3400.  
  3401. //-------------------- equals
  3402.  
  3403. boolean equals (Position p) {return p != null && p.x == x && p.y == y;}
  3404.  
  3405. } // end class Position
  3406.  
  3407.  
  3408. //------------------------------------------------------------------------------
  3409. //
  3410. // inner class Hill: a hill on the board
  3411. //
  3412. //------------------------------------------------------------------------------
  3413.  
  3414. static class Hill implements Serializable // must be serializable: sent to opponent and written on recording
  3415. {
  3416. private Position pos, center; // its position (upper left corner of bounding box) and center
  3417. private int diameter, radius; // diameter and radius
  3418. private float height; // height
  3419. private int rings; // elevation rings when displayed
  3420.  
  3421. //--------------- Constructor: just set the fields
  3422.  
  3423. Hill (Position pos, int diameter, int rings)
  3424. {this.pos=pos;
  3425. this.diameter=diameter;
  3426. this.rings = rings;
  3427. radius = diameter/2; // radius is half diameter
  3428. height = 1.1f + ((float)rings)/10; // height is 1.1 + 0.1*number of rings
  3429. center = new Position(pos.x+radius, pos.y+radius); // calculate center
  3430. }
  3431.  
  3432. //-------------- invalid: returns true if the hill is invalid
  3433.  
  3434. boolean invalid (Hill [] hills, Area boardArea) // params are the hills generated so far
  3435. {
  3436. boolean r = false;
  3437. if (!boardArea.contains(pos.x,pos.y,diameter,diameter)) // If the board are does not contain the bounding box, then true
  3438. return true;
  3439. for (Hill otherHill : hills) // Ow, for all hills generated so far
  3440. {if (otherHill != null & otherHill != this)
  3441. r |= otherHill.center.distance(center) // Check that the distance between centers is larger than hill extents
  3442. < radius + otherHill.radius +1;
  3443. }
  3444. return r;
  3445. }
  3446.  
  3447. // ------------- onIt: returns true if the position is on the hill
  3448.  
  3449. boolean onIt(Position p)
  3450. {return p.distance(center) <= radius;} // p is closer than radius to center
  3451.  
  3452.  
  3453. // ------------ draw: draw me
  3454.  
  3455. private void draw (Graphics2D g, int SIZE) // param SIZE needed to compute ring distance
  3456. {
  3457. final int distCirc = SIZE/12; // ring distance is 1/12 of SIZE
  3458. g.setColor(Color.BLACK); // draw with black color
  3459. int d = 0;
  3460. int i=0;
  3461. while (i<rings & diameter > d) // for each ring
  3462. {g.drawOval(pos.x+d/2, pos.y+d/2,diameter-d,diameter-d); // draw it
  3463. i++;
  3464. d += distCirc;}
  3465. }
  3466. } // end class Hill
  3467.  
  3468. //---------------------------------------------------------------------------
  3469. // Board Constructor just sets things up
  3470. //---------------------------------------------------------------------------
  3471.  
  3472. Board (boolean isLite,
  3473. boolean withHills,
  3474. int graphics,
  3475. int shape,
  3476. int boardS,
  3477. int height, // params are board dimensions
  3478. int width,
  3479. int pebbles,
  3480. boolean iPlayWhite) // and initial number of pebbles
  3481. {
  3482.  
  3483. //------------- Set various parameters
  3484.  
  3485. WIDTH = width; // remember params from invocation
  3486. HEIGHT = height;
  3487. this.shape = shape;
  3488. this.iPlayWhite=iPlayWhite;
  3489. this.isLite = isLite;
  3490. this.withHills = withHills;
  3491. circleBoard = boardS == 2;
  3492. whiteremaining = blackremaining = pebbles; // Set initial number of available Pebbles
  3493.  
  3494. if (graphics==1) SIZE = 60; // set the SIZE according to graphics parameter
  3495. else if (graphics==2) SIZE = 80;
  3496. else SIZE=100;
  3497.  
  3498. SIZE = (SIZE * 8) / Math.max(WIDTH, HEIGHT); // calculate square size in pixels
  3499.  
  3500. if (circleBoard) // If a circular board
  3501. {Ellipse2D.Float boardShape = new Ellipse2D.Float(); // then declare a shape for it
  3502. boardShape.setFrame(SIZE, SIZE, SIZE*WIDTH, SIZE*HEIGHT); // set its position and size parameters
  3503. boardArea = new Area(boardShape); // and define the area for this shape
  3504.  
  3505. topLeft = new Position(SIZE*(WIDTH+2)/2,SIZE+1); // the initial piece positions are here
  3506. bottomRight = new Position(SIZE *(WIDTH+2)/2, // the top and bottom positions of the ellipse
  3507. SIZE*(HEIGHT+1)-1);
  3508. }
  3509. else // If a rectangular board
  3510. {Rectangle2D.Float boardShape = new Rectangle2D.Float(); // then declare a shape for it
  3511. boardShape.setFrame(SIZE, SIZE, SIZE*WIDTH, SIZE*HEIGHT); // set its position and size parameters
  3512. boardArea = new Area(boardShape); // and define the area for this shape
  3513.  
  3514. topLeft = new Position(SIZE,SIZE); // initial positions are
  3515. bottomRight = new Position(SIZE *(WIDTH+1)-1, // opposite corners of the board
  3516. SIZE*(HEIGHT+1)-1);}
  3517.  
  3518.  
  3519. //--------------- initialise various variables
  3520.  
  3521. myArea = new Area();
  3522. opponentArea = new Area();
  3523. opponentVisibleArea = new Area();
  3524. neutralArea = new Area();
  3525. debugRegions = new ArrayList();
  3526. highlightArea = new Area();
  3527. buildableArea = new Area();
  3528.  
  3529. pieces = new ArrayList();
  3530.  
  3531. newPiece (Piece.Type.PEBBLE, true, iPlayWhite, topLeft); // initial white piece
  3532. newPiece (Piece.Type.PEBBLE, false,!iPlayWhite, bottomRight); // initial black piece
  3533.  
  3534. initialselectors = new Piece[5]; // set up selector pieces
  3535. int selectorPosX = SIZE/2; // x-coordinate of selector pieces
  3536.  
  3537. initialselectors[0] = newPiece (Piece.Type.PEBBLE,true,true, new Position(selectorPosX,2*SIZE));
  3538. initialselectors[1] = newPiece (Piece.Type.RUBBLE,true,true ,new Position(selectorPosX,3*SIZE));
  3539. initialselectors[2] = newPiece (Piece.Type.NIMBLER,true,true ,new Position(selectorPosX,4*SIZE));
  3540. initialselectors[3] = newPiece (Piece.Type.QUORUM,true,true ,new Position(selectorPosX,5*SIZE));
  3541. initialselectors[4] = newPiece (Piece.Type.BOUNCER,true,true ,new Position(selectorPosX,6*SIZE));
  3542.  
  3543. myPebble = (Pebble)initialselectors[0]; // For drawing remaining pebbles use the pebble selector!
  3544.  
  3545. for (Piece selector : initialselectors) selector.setAreas();
  3546.  
  3547. //-------------- Set up textures for the graphics
  3548.  
  3549.  
  3550. textureRect = new Rectangle(0,0,10,10); // 10x10 texture rectangle, used for buildable areas
  3551.  
  3552.  
  3553. //-------------- Texture for buildableArea controlled by white
  3554.  
  3555. whiteBuild = new BufferedImage(10, 10,
  3556. BufferedImage.TYPE_INT_RGB);
  3557. Graphics2D bigWb = whiteBuild.createGraphics();
  3558. bigWb.setColor(WHITECONTROLLED_2);
  3559. bigWb.fill(textureRect);
  3560. bigWb.setStroke(new BasicStroke(1));
  3561. bigWb.setColor(WHITEBUILD);
  3562. bigWb.drawOval(0,0,10,10);
  3563.  
  3564. //-------------- Texture for buildableArea controlled by black
  3565.  
  3566. blackBuild = new BufferedImage(10, 10,
  3567. BufferedImage.TYPE_INT_RGB);
  3568. Graphics2D bigBb = blackBuild.createGraphics();
  3569. bigBb.setColor(BLACKCONTROLLED_2);
  3570. bigBb.fill(textureRect);
  3571. bigBb.setStroke(new BasicStroke(1));
  3572. bigBb.setColor(BLACKBUILD);
  3573. bigBb.drawOval(0,0,10,10);
  3574.  
  3575.  
  3576. //-------------- The buffered image for the board (note it includes the margins)
  3577.  
  3578. boardImage = new BufferedImage((WIDTH+2)*SIZE, (HEIGHT+2)*SIZE,
  3579. BufferedImage.TYPE_INT_RGB);
  3580. boardGraphics = boardImage.createGraphics();
  3581.  
  3582. }
  3583.  
  3584. //------------------- end of board constructor ----------------------------------
  3585.  
  3586.  
  3587.  
  3588.  
  3589. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3590. // Utility functions: check if position is on the board.
  3591. // Determine top active selector
  3592. // methods to access and check remaining pebbles
  3593. // methods to manipulate pieces
  3594. // etc....
  3595. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3596.  
  3597. //--------- generate the Hills --------------------------------------------
  3598.  
  3599.  
  3600.  
  3601.  
  3602. // -------------- Generate all the hills ------------------------------------------
  3603.  
  3604. Hill [] generateHills()
  3605. { Random rand = new Random(); // get me a new random number generator
  3606. Hill hill; // local hill. I'll generate a hill there and
  3607. // use it only if it doesn't collide with other hills
  3608. // or the border
  3609. int numberOfHills = (rand.nextInt(maxHills-minHills+1)+minHills) // determine the number of Hills
  3610. * WIDTH*HEIGHT/64, // scaled to board size in squares
  3611. sizeMin = (int) (SIZE*minHillSize), // minimum hill size
  3612. sizeMax = (int) (SIZE*maxHillSize); // maximum hill size
  3613.  
  3614. hills = new Hill [numberOfHills]; // get the array for hills
  3615. for (int i = 0; i<numberOfHills; i++) // for each element in the array,
  3616. {do
  3617. {int x = 2*SIZE + rand.nextInt(SIZE*(WIDTH-2)), // determine the position
  3618. y = 2*SIZE + rand.nextInt(SIZE*(HEIGHT-2)),
  3619. d = rand.nextInt(sizeMax - sizeMin) + sizeMin, // and diameter
  3620. rings = rand.nextInt(5)+1; // and height
  3621. hill = new Hill(new Position(x,y),d,rings); // and create this hill
  3622. }
  3623. while (hill.invalid(hills, boardArea)); // repeat this until the hill checks out
  3624. hills[i]=hill; // then save it in the array
  3625. }
  3626. return hills; // return the result (also saved in hills)
  3627. }
  3628.  
  3629.  
  3630. //----------- let someone else tell me what the hills should be
  3631.  
  3632. void setHills (Hill [] hills) // just store the parameter in hills
  3633. {this.hills = hills;}
  3634.  
  3635. //----------------Checks if position is on the board----------------------------------
  3636.  
  3637. boolean isLegal(Position p)
  3638. {return (p!= null) && boardArea.contains(p.x, p.y);} // A position is legal if non null and on the board
  3639.  
  3640. //----------------Elevation: more than 1 if on a hill ----------------------------------
  3641.  
  3642. float elevation(Position pos)
  3643. {for (Hill hill : hills) // for every hill
  3644. if (hill.onIt(pos)) return hill.height; // if on it then return the hill height
  3645. return 1f; // if not on any hill return 1
  3646. }
  3647.  
  3648.  
  3649. //----------------Set the default selector-----------------------------------------
  3650.  
  3651. Piece defaultselector() // default selector is topmost available selector
  3652. {for (int y=0; y<5; y++) // y runs through selector idx
  3653. if (initialselectors[y].shouldShowSelector()) // if selector y is available
  3654. {selectorIdx = y; // then this is it!
  3655. return initialselectors[y];}
  3656. return null;}; // nothing available
  3657.  
  3658. //----------------Cycle through selectors-----------------------------------------
  3659.  
  3660. Piece cycleSelector()
  3661. {int y; // y cycles from selectorIdx+1 to selectorIdx
  3662. if (selectorIdx==4) y=0; else y = selectorIdx+1; // going from 2 to 6
  3663. while (y != selectorIdx)
  3664. {if (initialselectors[y].shouldShowSelector()) // if y is available
  3665. {selectorIdx = y; return initialselectors[y];} // then this is it!
  3666. if (y==4) y=0; else y++;
  3667. }
  3668. if (initialselectors[y].shouldShowSelector()) // nothing found on one trip
  3669. return initialselectors[y]; else return null; // so let previous selector remain if possible
  3670. }
  3671.  
  3672. //----------------Find selector at position p-----------------------------------------
  3673.  
  3674. Piece select(Position p)
  3675. { Piece result = null;
  3676. for (int y=0; y<5 ; y++) // y runs through selector idx
  3677. { Piece piece = initialselectors[y];
  3678. if (piece.shouldShowSelector() // if the selector is shown
  3679. && piece.on(p)) // and is located on p
  3680. {selectorIdx = y; // then it is it.
  3681. result = piece;}
  3682. }
  3683. return result;
  3684.  
  3685.  
  3686. }
  3687.  
  3688.  
  3689. //-------------------Remaining number of pebbles---------------------------------
  3690.  
  3691. int remaining(boolean whiteside)
  3692. {return (whiteside ? whiteremaining : blackremaining) ;}
  3693.  
  3694. //-------------------Decrease remaining number of pebbles------------------------
  3695.  
  3696. void decreaseRemaining (boolean whiteside)
  3697. {if (whiteside) whiteremaining--; else blackremaining--;}
  3698.  
  3699. //-------------------Determine what position a mouse event refers to ------------------------
  3700.  
  3701.  
  3702. // We also check if the position is very near a piece
  3703. // If so, we return the position of that piece. This means when you click a piece you don't have to
  3704. // hit the exact center. An exception is when moving a piece only a very short
  3705. // distance, then the mouse release will happen when it is still very close to itself.
  3706. // Therefore an extra parameter of type Piece tells what piece should be exempt from
  3707. // this proximity check.
  3708.  
  3709. Position realPosition (int x, int y, Piece exemptPiece) // params are pixel coordinates and
  3710. { // the piece exempt from proximity check
  3711. Position res = new Position(x,y); // First create the proper position
  3712. {int min = 10000; // (min is initially bigger than any possible distance)
  3713. Piece closestPiece = null;
  3714. for (Piece thePiece : pieces)
  3715. { // It may be "on" several pieces
  3716. if (thePiece.on(res) // if so we must find the closest to res
  3717. & ! (thePiece == exemptPiece)
  3718. & res.distance(thePiece) < min)
  3719. {min = res.distance(thePiece); // found a new minimum, remember it
  3720. closestPiece = thePiece;}
  3721. }
  3722. if (closestPiece != null) return closestPiece.pos;
  3723. else return res;
  3724. }}
  3725.  
  3726.  
  3727. Position realPosition(int x, int y) {return (realPosition(x, y, (Piece)null));} // Ditto, without info about exempt piece
  3728.  
  3729. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3730. // create a new piece and add it to the board if it is on a legal board position
  3731. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3732.  
  3733. final Piece newPiece(Piece.Type pt, boolean w, boolean my, Position p)
  3734. {Piece res = Piece.newPiece(pt,w,my,p,this);
  3735. if (isLegal(p))
  3736. addPiece(res); // put it on the board, if on a legal position
  3737. return res;
  3738. }
  3739.  
  3740. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3741. // Add a piece to the board
  3742. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3743.  
  3744. Piece addPiece(Piece p)
  3745. {
  3746. Piece victim = find(p.pos); // If that spot is occupied
  3747. if (victim != null) removePiece(victim); // then remove the piece there
  3748. pieces.add(p); // add it to the list of pieces
  3749. p.setAreas(); // and make sure you calculate its areas
  3750. return p;
  3751. }
  3752.  
  3753. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3754. // Remove a piece from the board
  3755. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3756.  
  3757. void removePiece(Piece p)
  3758. {
  3759. pieces.remove(p); // just remove it from the list of pieces
  3760. }
  3761.  
  3762.  
  3763.  
  3764. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3765. // Find the piece at position x,y
  3766. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3767.  
  3768. Piece find(Position pos)
  3769. {
  3770. Piece result = null;
  3771. for (Piece piece : pieces) // For all pieces
  3772. if (piece.pos.equals(pos)) result = piece; // if its position is pos then it is it.
  3773. return result;
  3774. }
  3775.  
  3776.  
  3777. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3778. // Check if a controlled piece of certain type is near
  3779. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3780.  
  3781. // This only applies to Pebbles and Keeps. Rubbles instead use buildableArea
  3782.  
  3783.  
  3784. boolean near(Piece.Type pt, Position pos)
  3785. {boolean found = false;
  3786. for (int i=0; i<pieces.size() & !found; i++) // for all pieces
  3787. {Piece piece = (Piece)pieces.get(i);
  3788. found = piece.pieceType == pt // is it of right type?
  3789. && piece.myside // on my side?
  3790. && (pos.distance(piece) > SIZE*2/3 || pt != Piece.Type.PEBBLE) // not too close (pebble only)
  3791. && pos.distance(piece) < SIZE // not too far away
  3792. && myArea.contains(piece.pos.x, piece.pos.y); // piece controlled by me
  3793. }
  3794.  
  3795. if (found & pt == Piece.Type.PEBBLE) // for pebble, check that other pieces are not too close
  3796. for (int i=0; i<pieces.size() & found; i++)
  3797. {Piece piece = (Piece)pieces.get(i);
  3798. found = pos.distance(piece) > SIZE*2/3;
  3799. }
  3800.  
  3801. return found;
  3802. }
  3803.  
  3804. // Various checks on the properties of a position
  3805.  
  3806.  
  3807. boolean iControl(Position pos)
  3808. {return myArea.contains(pos.x, pos.y);}
  3809.  
  3810. boolean opponentControls(Position pos)
  3811. {return opponentArea.contains(pos.x, pos.y);}
  3812.  
  3813. boolean iControl(Piece p)
  3814. {return iControl(p.pos);}
  3815.  
  3816. boolean opponentControls(Piece p)
  3817. {return opponentControls(p.pos);}
  3818.  
  3819. boolean canMoveTo(Position pos)
  3820. {return highlightArea.contains(pos.x,pos.y);}
  3821.  
  3822. boolean canBuildPebbleOn(Position pos)
  3823. {return buildableArea.contains(pos.x,pos.y);}
  3824.  
  3825.  
  3826.  
  3827.  
  3828.  
  3829.  
  3830. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3831. //
  3832. // Methods for painting the board
  3833. //
  3834. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3835.  
  3836. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3837. //
  3838. // generateBoardGraphics: Genereate a buffered image of the board
  3839. //
  3840. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3841.  
  3842.  
  3843. void generateBoardGraphics(Piece currentPiece) // selected piece (will be highlighted)
  3844.  
  3845. { long startTime = System.currentTimeMillis(); // timing only for debugging purposes
  3846.  
  3847. // This will paint the buffered image of the board, omitting current position marker, clock, and selectors
  3848.  
  3849.  
  3850. BufferedImage bi = new BufferedImage((WIDTH+2)*SIZE, (HEIGHT+2)*SIZE, // get a new temporary image to draw on
  3851. BufferedImage.TYPE_INT_RGB);
  3852. Graphics2D g = bi.createGraphics();
  3853. g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  3854. RenderingHints.VALUE_ANTIALIAS_ON);
  3855.  
  3856.  
  3857. // Begin by drawing the background, a board where everything is invisible, and frame
  3858.  
  3859.  
  3860. g.setColor(BACKGROUNDCOLOR);
  3861. g.fillRect(0,0, SIZE*(WIDTH+2), SIZE*(HEIGHT+2));
  3862.  
  3863. g.setColor(darkInvisible);
  3864. g.fill(boardArea);
  3865.  
  3866. if (circleBoard)
  3867. {g.setColor(FRAMECOLOR);
  3868. g.setStroke(new BasicStroke(FRAMESIZE));
  3869. g.draw(boardArea);
  3870. g.setStroke(new BasicStroke(1));
  3871. }
  3872. else
  3873. {
  3874. g.setColor(FRAMECOLOR);
  3875. g.fillRect(SIZE-FRAMESIZE, SIZE-FRAMESIZE, SIZE*WIDTH+2*FRAMESIZE, FRAMESIZE);
  3876. g.fillRect(SIZE*(WIDTH+1), SIZE, FRAMESIZE, SIZE*HEIGHT);
  3877. g.fillRect(SIZE-FRAMESIZE, SIZE*(HEIGHT+1), SIZE*WIDTH+2*FRAMESIZE, FRAMESIZE);
  3878. g.fillRect(SIZE-FRAMESIZE, SIZE, FRAMESIZE, SIZE*HEIGHT);
  3879. }
  3880.  
  3881. //-------- Now generate areas for white, black and on
  3882.  
  3883. Area whiteArea = iPlayWhite ? myArea : opponentVisibleArea;
  3884. Area blackArea = iPlayWhite ? opponentVisibleArea : myArea;
  3885.  
  3886.  
  3887. //-------- Paint the areas: my, opponent's and neutral (visible)
  3888.  
  3889.  
  3890. g.setColor(NEUTRAL);
  3891. if (allVisible) g.fill(boardArea); else g.fill(neutralArea);
  3892.  
  3893. g.setColor(BLACKCONTROLLED);
  3894. g.fill(blackArea);
  3895.  
  3896. g.setColor(WHITECONTROLLED);
  3897. g.fill(whiteArea);
  3898.  
  3899.  
  3900.  
  3901. //--------- Paint the buildable area
  3902.  
  3903. g.setColor(iPlayWhite ? WHITEBUILD : BLACKBUILD);
  3904. g.setPaint(new TexturePaint(iPlayWhite ? whiteBuild : blackBuild, textureRect));
  3905. g.fill(buildableArea);
  3906.  
  3907. //---------- Paint the highlighted area
  3908.  
  3909. g.setColor(iPlayWhite ? WHITEHIGHLIGHT : BLACKHIGHLIGHT);
  3910. g.fill(highlightArea);
  3911.  
  3912. //---------- Paint the hills
  3913.  
  3914. if (withHills)
  3915. for (Hill hill : hills) hill.draw(g,SIZE);
  3916.  
  3917. //----------- Now paint all pieces
  3918.  
  3919. for (Piece thePiece : pieces) // For all pieces
  3920. {
  3921. if (thePiece == currentPiece) thePiece.draw(g, Color.red); //Currently grabbed piece is red
  3922. else if (thePiece.myside || // OW, determine if it is visible
  3923. myArea.contains(thePiece.pos.x, thePiece.pos.y) ||
  3924. neutralArea.contains(thePiece.pos.x, thePiece.pos.y) ||
  3925. opponentVisibleArea.contains(thePiece.pos.x, thePiece.pos.y)
  3926. ) thePiece.draw(g); // if it is then draw it
  3927.  
  3928. }
  3929.  
  3930. //----------- Paint line of remaining pebbles
  3931.  
  3932. if (allVisible) // if allVisible then draw both white and black Pebbles
  3933. {for (int i=1; i<= remaining(true); i++)
  3934. myPebble.draw(g, new Position(i*SIZE+SIZE/2,SIZE/2), Color.white);
  3935. for (int i=1; i<= remaining(false); i++)
  3936. myPebble.draw(g,new Position( i*SIZE+SIZE/2, SIZE*(HEIGHT +1) + SIZE/2), Color.black);}
  3937. else // else only show my Pebbles
  3938. for (int i = 1; i<=remaining(iPlayWhite); i++)
  3939. myPebble.draw(g, new Position(i*SIZE+SIZE/2, SIZE/2), iPlayWhite ? Color.white : Color.black);
  3940.  
  3941.  
  3942.  
  3943. //------------ Show who controls most area
  3944.  
  3945. if (iPlayWhite) g.setColor(Color.white); else g.setColor(Color.black);
  3946. g.setFont(myFont);
  3947. if (relativeNumberOfControlledSquares<0) g.setColor(Color.red);
  3948. g.drawString(relativeNumberOfControlledSquares+" ",SIZE/2,SIZE/2);
  3949.  
  3950. //--------------- show selection pieces
  3951.  
  3952. if (!playBackBoard) // In a playback no selectors are shown.
  3953.  
  3954. for (Piece selector : initialselectors) {
  3955. if (selector.shouldShowSelector())
  3956. selector.drawOutline(g, (iPlayWhite ? Color.white : Color.black));
  3957. }
  3958.  
  3959. //--------------debug: show regions
  3960.  
  3961. if (debug)
  3962. { g.setColor(Color.green);
  3963. for (Region region : debugRegions)
  3964. g.draw(region.area);
  3965. }
  3966.  
  3967.  
  3968. // -------------- Finally, paint all this on the proper board image
  3969.  
  3970. boardGraphics.drawImage(bi,null,0,0);
  3971. timeGraphics = (int)(System.currentTimeMillis() - startTime);
  3972. }
  3973.  
  3974. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3975. //
  3976. // paint: paints the board,using the buffered image
  3977. //
  3978. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3979.  
  3980. void paint (Graphics g, // Where to paint it
  3981. boolean waiting, // true if timer not expired, so player cannot move
  3982. boolean stalling, // true if "Stalling" should be shown
  3983. int clock // number of seconds remaining
  3984. )
  3985.  
  3986.  
  3987. {
  3988. Graphics2D page = (Graphics2D)g;
  3989. page.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  3990. RenderingHints.VALUE_ANTIALIAS_ON);
  3991. page.drawImage(boardImage,null,0,0); // render the buffered image
  3992.  
  3993. // --------------- Show if I can move
  3994.  
  3995. if (!waiting & !playBackBoard) myPebble.draw(page,(new Position(SIZE/2,SIZE*(HEIGHT+1))), Color.red);
  3996.  
  3997. // ---------------- Show clock
  3998.  
  3999. page.setColor(Color.black);
  4000. page.setFont(clockFont);
  4001. if (clock <= 10) // close to timout: clock is red and larger
  4002. {page.setColor(Color.red);
  4003. page.setFont(myFont);}
  4004. page.drawString(clock/60 + ":" + (clock%60)/10 + (clock%60)%10,
  4005. (SIZE*WIDTH+SIZE/2),SIZE/2);
  4006.  
  4007. // ----------------- Some diagnostics and debug info
  4008. if (debug)
  4009. {page.drawString("U: "+numberOfUncontested+" ", SIZE*(WIDTH+1), SIZE*(HEIGHT-1)/2);
  4010. page.drawString("R: "+numberOfRegions+" ", SIZE*(WIDTH+1), SIZE*HEIGHT/2);
  4011.  
  4012. page.drawString("B: " + numberOfBox, SIZE*(WIDTH+1), SIZE*(HEIGHT+1)/2) ;
  4013. page.drawString("I: " + numberOfIntersection, SIZE*(WIDTH+1), SIZE*(HEIGHT+2)/2) ;
  4014. page.drawString("Ct: "+ timeCalculate, SIZE*(WIDTH+1),SIZE*(HEIGHT+4)/2);
  4015. page.drawString("It: "+ timeInit, SIZE*(WIDTH+1),SIZE*(HEIGHT+5)/2);
  4016. page.drawString("Mt: "+ timeMy, SIZE*(WIDTH+1),SIZE*(HEIGHT+6)/2);
  4017. page.drawString("Ht: "+ timeOpponents, SIZE*(WIDTH+1),SIZE*(HEIGHT+7)/2);
  4018. }
  4019.  
  4020. // ------------------ show stall message
  4021.  
  4022. if (stalling)
  4023. {page.setColor(Color.red);
  4024. page.setFont(stallFont);
  4025. page.drawString("S T A L L I N G",SIZE*3,SIZE/2);
  4026. }
  4027. }
  4028.  
  4029.  
  4030.  
  4031. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4032. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4033. // Recalculate control of each area
  4034. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4035. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4036.  
  4037.  
  4038. //-------------------------------------------------
  4039. // A piece is uncontested if its range does not intersect the range of any opposite piece
  4040. //-------------------------------------------------
  4041.  
  4042. private boolean uncontested(Piece p)
  4043. {boolean res = true; // result will be true unless we find a
  4044. for (Piece other : pieces) // piece, call it other
  4045. {if ((p.myside != other.myside) // playing for the opposite side
  4046. && p.currentRange + other.currentRange >= p.pos.distance(other)) // and so close that the range areas overlap
  4047. res = false;
  4048. }
  4049. return res;
  4050. };
  4051.  
  4052.  
  4053. //-----------------------------------------------
  4054. // Inner class Region: a part of the board
  4055. //-----------------------------------------------
  4056.  
  4057. // During the recalculation of control areas the board is partitioned into regions. The regions are formed
  4058. // by intersections of the piece range areas.
  4059. // Each region has an Area which it occupies, an integer control value which is the amount of control
  4060. // exerted on it so far (positive for my side, negative for opponent, zero for neutral), a boolean visibility
  4061. // indicator which tells if that region should be painted on the board, and Rectangle bounding box.
  4062. // The reason for the latter is that it is more efficient to perform some of the computations on
  4063. // bounding boxes before emabarking on more time consuming calculations on Areas.
  4064.  
  4065.  
  4066. private class Region
  4067. { Area area; // the area on the board of the region
  4068. int control; // control degree measured so far (negative = opponent)
  4069. boolean visible; // true if visible
  4070. Rectangle boundingBox; // bounding box of the area
  4071. Position center; // center of the bounding box
  4072. float radius; // radius of the bounding box. So all positions in the area is within radius of the center.
  4073.  
  4074. //----- Constructor just sets things up and calculates the bounding box
  4075.  
  4076. private Region (Area a, int c, boolean v)
  4077. {area = a; control = c; visible = v; setBounds();}
  4078.  
  4079.  
  4080.  
  4081. //------ (re)calculate the bounding box, its center and radius
  4082.  
  4083. private void setBounds()
  4084. {boundingBox = area.getBounds();
  4085. center = new Position (boundingBox.x + boundingBox.width/2, boundingBox.y+boundingBox.height/2);
  4086. radius = center.distance(new Position(boundingBox.x, boundingBox.y));
  4087. }
  4088.  
  4089.  
  4090. //------------ decided: check if my control can possibly fall to zero or below
  4091.  
  4092.  
  4093. private boolean decided (ArrayList<Piece> pieces) // parameter is opponent pieces to take into consideration
  4094. {
  4095. int c = control; // let c be the lowest control I can possibly reach
  4096. for (Piece inspected : pieces) { // as long as c is positive, and for all pieces
  4097. if (center.distance(inspected) <= inspected.currentRange + radius) // if it is so close that it might have an influence on my area
  4098. c -= inspected.power; // decrease c by its power
  4099. }
  4100. return c>0; // return true iff c is still positive
  4101. }
  4102.  
  4103. //------------- addMe: add me to a list of regions unless my fate can already be inferred
  4104.  
  4105. // The list of regions is the list of still undecided regions who need further examination
  4106. //
  4107. // If my control is negative it can only decrease, so add me to his area
  4108. // else if it is decided (cannot become non-positive) then add me to my area
  4109. // else keep me in the list of regions
  4110.  
  4111. private void addMe(ArrayList<Region> regList, ArrayList<Piece> pieces)
  4112. { if (control < 0) // if control <0 then
  4113. {opponentArea.add(area); // it can only decrease further so
  4114. if (debug) debugRegions.add(this); // add it to his area and
  4115. if (visible) opponentVisibleArea.add(area);} // not to the region list
  4116. else // if control >= 0
  4117. if (decided(pieces)) // and the area is deceidedly mine
  4118. {myArea.add(area);if (debug) debugRegions.add(this);} // then add it to my area
  4119. else regList.add(this); // else add it to the list of regions
  4120. }
  4121.  
  4122. //------------- checkPiece--------------------------
  4123. //
  4124. // This determines what happens to the me when a Piece is taken into consideration.
  4125. // If the range area completely encloses me, then update the control value with the amount
  4126. // exerted by the piece. If the range area is disjoint from me then do nothing. If it intersects me
  4127. // then create a new region corresponding to the intersection, and subtract it from my area. For
  4128. // efficiency the predicates are first tested on my bounding box.
  4129. // The resulting regions are accumulated in the second parameter. The third
  4130. // parameter is a list of enemy pieces to consider when determining if the region is decided.
  4131.  
  4132. private void checkPiece(Piece p, ArrayList<Region> newRegions, ArrayList<Piece> pieces)
  4133. { if (center.distance(p) > radius + p.currentRange) newRegions.add(this); // area completely out of range: just forward the area to newRegions
  4134. else
  4135. if
  4136. (p.rangeArea.contains(boundingBox)) // If my bounding box lies completely in range
  4137. {control += p.power * (p.myside?1:-1); numberOfBox++;// update my control value with the power of the piece
  4138. addMe(newRegions, pieces);} // and add me to the new regions
  4139. else if ( p.rangeArea.intersects(boundingBox)) // else, if the bounding box intersects the range
  4140. {Area newArea = p.rangeArea(); // create a new area that is the intersection
  4141. newArea.intersect(area); // with the range area
  4142. numberOfIntersection++; // (debug)
  4143. if (newArea.equals(area)) // if that turns out to be the whole area
  4144. {control += p.power * (p.myside?1:-1); // just update my control with the piece power
  4145. addMe(newRegions, pieces);} // and add me to the new regions
  4146. else if (!newArea.isEmpty()) // else, if that turns out to be nonempty
  4147. {area.subtract(newArea); // subtract it from my area
  4148. setBounds(); // recalulate my bounding box
  4149. addMe(newRegions, pieces); // and add me to newRegions
  4150. Region newRegion = (new Region(newArea, // and create a new region for the intersection
  4151. control + p.power * (p.myside?1:-1),// with updated power
  4152. p.myside | visible)); // and visibility the same as before, or true if piece on my side
  4153. newRegion.addMe(newRegions,pieces); // and invoke its addMe for adding it to newRrgions
  4154. } // if the intersection is empty
  4155. else addMe(newRegions, pieces); // then just forward me to new Regions
  4156. } // also if intersection of bounding box is empty
  4157. else addMe(newRegions, pieces);
  4158. numberOfBox++;
  4159. }
  4160. }
  4161.  
  4162. //-----------------end class Region
  4163.  
  4164.  
  4165. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4166. // calculateControl: determine myArea, opponentArea, opponentVisibleArea, onAreas. Also count number of pieces on board
  4167. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4168.  
  4169. void calculateControl()
  4170.  
  4171. //---------- first reset and initialise variables
  4172.  
  4173. { long startTime = System.currentTimeMillis();
  4174.  
  4175.  
  4176.  
  4177. initPiecesOnBoard(); // reset variables and areas
  4178. myArea.reset();
  4179. opponentArea.reset();
  4180. opponentVisibleArea.reset();
  4181. numberOfBox=0;
  4182. numberOfIntersection=0;
  4183. neutralArea.reset();
  4184. numberOfUncontested=0;
  4185. debugRegions.clear();
  4186.  
  4187. ArrayList<Piece> myPieces = new ArrayList(); // the subset of pieces that are mine
  4188. ArrayList<Piece> opponentPieces = new ArrayList(); // the subset of pieces that are the opponent's
  4189.  
  4190. ArrayList<Region> regions = new ArrayList(); // initialise the "regions" to contain just one,
  4191. regions.add(new Region((Area)boardArea.clone(),0,allVisible)); // the entire board with control 0
  4192.  
  4193. // Sort pieces into mine and opponent's, update number records,
  4194. // Let uncontested pieces exert directly (these will not need to generate regions)
  4195.  
  4196. for (Piece inspected : pieces)
  4197. {
  4198. if (inspected.myside) // if it is my piece
  4199. {
  4200. incrementPieceOnBoard(inspected.pieceType); // update the record of number of pieces
  4201. {if (!uncontested(inspected)) myPieces.add(inspected); // add it to the list of my pieces
  4202. else {myArea.add(inspected.rangeArea); // or, if uncontested, add the range area directly
  4203. numberOfUncontested++;}
  4204. }
  4205. }
  4206. else // if it is opponent's piece
  4207. {
  4208. if (!uncontested(inspected))opponentPieces.add(inspected); // and add it to the list of opponent pieces
  4209. else // or, if uncontested
  4210. {opponentArea.add(inspected.rangeArea); numberOfUncontested++;
  4211. if (allVisible) opponentVisibleArea.add(inspected.rangeArea); // add it to opponentArea or opponentVisibleArea
  4212. }
  4213. }
  4214. } // end for each piece
  4215.  
  4216. timeInit = (int)(System.currentTimeMillis() - startTime);
  4217.  
  4218. //----------- check the influence of all my contested pieces on the control areas
  4219. //
  4220. // This will generate regions of increasing positive control, as the range areas intersect.
  4221.  
  4222. long s2 = System.currentTimeMillis();
  4223.  
  4224. for (Piece inspected : myPieces) // for each of my pieces
  4225. {
  4226. ArrayList<Region> newRegions = new ArrayList(); // the regions that need to be examined after this
  4227. for (Region thisRegion : regions) // for each existing region
  4228. thisRegion.checkPiece(inspected, newRegions, opponentPieces); // let the region check the piece for possible influence on its area and control // end for all regions
  4229. regions = newRegions; // continue with the newly generated regions
  4230. } // end for each of my pieces
  4231.  
  4232. timeMy = (int)(System.currentTimeMillis() - s2);
  4233.  
  4234. //--------- check the influence of opponent pieces on the control areas
  4235. //
  4236. // This will generate regions with decreasing control.
  4237.  
  4238. s2 = System.currentTimeMillis();
  4239. while (!opponentPieces.isEmpty()) // for each of opponent's pieces
  4240. {Piece inspected = (Piece)opponentPieces.get(0); // call it inspected
  4241. opponentPieces.remove(inspected); // remove it from opponentPieces (so it cannot unduly affect the screening by "decided")
  4242. ArrayList<Region> newRegions = new ArrayList(); // reset the new regions to be generated
  4243. for (Region thisRegion : regions) // for each region
  4244. thisRegion.checkPiece(inspected, newRegions, opponentPieces); // let the region check the piece for possible influence on its area and control
  4245. regions = newRegions; // continue with the newly generated regions
  4246. } // end for each of his pieces
  4247. if (debug) debugRegions.addAll(regions);
  4248. numberOfRegions = debugRegions.size();
  4249. timeOpponents = (int)(System.currentTimeMillis() - s2);
  4250. s2 = System.currentTimeMillis();
  4251.  
  4252. //-------- collect the regions into the main variables
  4253.  
  4254. for (Region thisRegion : regions)
  4255. {
  4256. if (thisRegion.control>0) myArea.add(thisRegion.area);
  4257. if (thisRegion.control == 0 & thisRegion.visible) neutralArea.add(thisRegion.area);
  4258. if (thisRegion.control < 0 & thisRegion.visible) opponentVisibleArea.add(thisRegion.area);
  4259. if (thisRegion.control < 0) opponentArea.add(thisRegion.area);
  4260. }
  4261.  
  4262. s2 = System.currentTimeMillis();
  4263.  
  4264. calculateRelativeNumberOfSquares(); // finally calculate who controls most territory
  4265.  
  4266. timeInit = (int)(System.currentTimeMillis() - s2);
  4267. timeCalculate = (int)(System.currentTimeMillis() - startTime);
  4268.  
  4269. }
  4270.  
  4271.  
  4272. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4273. // calculateRelativeNumberOfSquares: check who controls most territory
  4274. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4275.  
  4276. // result is stored in the global variable relativeNumberOfControlledSquares.
  4277. // the algorithm is to generate a grid of points (of cardinality numberOfSamples^2)
  4278. // and for each point check membership in myArea or hisArea.
  4279.  
  4280.  
  4281.  
  4282.  
  4283. private void calculateRelativeNumberOfSquares()
  4284.  
  4285. {final float sampleIncrementX = (SIZE*WIDTH)/numberOfSamples; // distance between sample points
  4286. final float sampleIncrementY = (SIZE*HEIGHT)/numberOfSamples;
  4287. final float scaleFactor = numberOfSamples*numberOfSamples/HEIGHT/WIDTH; // to scale the result down to something meaningful
  4288. relativeNumberOfControlledSquares=0;
  4289. for (float i=SIZE; i<=SIZE*(WIDTH+1); i=i+sampleIncrementX) // so just check all sample points
  4290. for (float j=SIZE; j<=SIZE*(HEIGHT+1); j=j+sampleIncrementY)
  4291. {if (myArea.contains(i,j)) relativeNumberOfControlledSquares++;
  4292. else if (opponentArea.contains(i,j)) relativeNumberOfControlledSquares--;
  4293. }
  4294. relativeNumberOfControlledSquares = Math.round(((float)(relativeNumberOfControlledSquares)/(float)scaleFactor));
  4295. }
  4296.  
  4297.  
  4298.  
  4299. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4300. // Recalculate buildble squares
  4301. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4302.  
  4303. void calculateBuildable()
  4304.  
  4305.  
  4306. { long startTime = System.currentTimeMillis();
  4307. buildableArea.reset();
  4308. if (myPebble.shouldShowSelector()) // buildable only if I can build a Pebble
  4309. {for (Piece inspected : pieces) // For each piece
  4310. {
  4311. if ( inspected.pieceType == Piece.Type.RUBBLE // if it is a Rubble
  4312. & inspected.myside // on my side
  4313. & myArea.contains(inspected.pos.x,inspected.pos.y) // and controlled by me
  4314. ) buildableArea.add(inspected.nearArea()); // add its near area to buildable
  4315. }
  4316. buildableArea.intersect(myArea); // but buildable area must be controlled
  4317. for (Piece inspected : pieces) // For each piece
  4318. buildableArea.subtract(inspected.avoidBuildArea);
  4319. timeBuildable = (int)(System.currentTimeMillis() - startTime);
  4320. }
  4321. }
  4322.  
  4323.  
  4324.  
  4325. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4326. // Recalculate highlighted area
  4327. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4328.  
  4329. void calculateHighlight(Piece currentPiece)
  4330. { long startTime = System.currentTimeMillis();
  4331. for (Piece inspected : pieces) // For each piece
  4332. inspected.underAttack = false; // set its "underAttack" to false
  4333.  
  4334.  
  4335. if (currentPiece != null) // only if there is a current piece
  4336. {highlightArea = currentPiece.strideArea(); // begin by highlighting its stride area
  4337. highlightArea.intersect(myArea); // that is in my control
  4338.  
  4339. for (Piece inspected : pieces) // For each enemy piece
  4340. if (!inspected.myside & highlightArea.contains(inspected.pos.x,inspected.pos.y)) // that sits in this area
  4341. inspected.underAttack=true; //set the underAttack flag
  4342.  
  4343.  
  4344. for (Piece inspected : pieces) // For each piece except the currentPiece
  4345. if (inspected != currentPiece) highlightArea.subtract(inspected.avoidArea(currentPiece)); // subtract the avoidArea.
  4346. }
  4347. else highlightArea.reset(); // no current piece: higlightArea is null
  4348. timeHighlight = (int)(System.currentTimeMillis() - startTime);
  4349. generateBoardGraphics(currentPiece); // this always prompts a regeneration of board graphics
  4350.  
  4351. }
  4352.  
  4353.  
  4354.  
  4355. }// end class Board
  4356.  
  4357.  
  4358.  
  4359.  
  4360.  
  4361.  
  4362.  
  4363.  
  4364. //*******************************************************************
  4365. //
  4366. // A piece
  4367. //
  4368. //*******************************************************************
  4369.  
  4370. abstract class Piece
  4371.  
  4372. {
  4373. // ----------- The following parameters uniquely determine the piece type. Change them when devising new pieces.
  4374.  
  4375. Type pieceType; // Type of piece
  4376. int range; // Range (how far control extends)
  4377. int currentRange; // range at this position (may be increased by a hill)
  4378. int power; // Power (how much control)
  4379. int stride; // stride (how far it can move)
  4380. int extent; // extent (how much space it occupies )
  4381. int moveTime; // delay after piece moved (units of Nimbler move)
  4382. int buildTime; // delay after piece built
  4383. int MAXIMUM; // Max number of pieces allowed simultaneously on the board of a type
  4384.  
  4385. Area rangeArea, // Area that piece controls
  4386. strideArea, // Area it can move to
  4387. extentArea, // Area it occupies (used to determine when you click on the piece)
  4388. nearArea, // only for rubbles, used to determine buildable area
  4389. avoidBuildArea; // Area too close to permit builds
  4390. boolean moveable; // true if can move
  4391.  
  4392. // ------------ Fields used for game state---------------------------------------------------------------------------
  4393.  
  4394. boolean whiteside; // true if white
  4395. boolean myside; // true if my, false if opponent's
  4396.  
  4397. Board.Position pos; // where it sits on the board
  4398. boolean underAttack; // true if it is under enemy control
  4399.  
  4400. Board board; // the board where the piece sits
  4401.  
  4402. Piece outline = this; // The outline to show when dragging it (usually itself)
  4403.  
  4404. private int nPoly; // graphics for range areas etc
  4405. private float[] xPoly;
  4406. private float[] yPoly;
  4407.  
  4408.  
  4409.  
  4410. final static private Color WHITE_NEUTRAL = new Color(210,210,210); // Various colors of pieces when not controlled by owner
  4411. final static private Color WHITE_OPPONENT = new Color (180,210,250);
  4412. final static private Color BLACK_NEUTRAL = new Color (80,80,80);
  4413. final static private Color BLACK_OPPONENT = new Color (130,80,80);
  4414.  
  4415. enum Type
  4416. {PEBBLE(0), RUBBLE(1), KEEP(2), BOUNCER(3), QUORUM(4), NIMBLER(5); // The six kinds of pieces
  4417. int index; // each kind has an integer index
  4418. Type(int idx) {
  4419. index = idx;}
  4420. }
  4421.  
  4422.  
  4423. static Piece newPiece(Type pt, boolean w, boolean my, Board.Position p, Board b) // create new piece of right type
  4424. {
  4425. Piece res = null; // ow compiler warns it may be undefined
  4426. switch (pt)
  4427. {case PEBBLE: res= new Pebble( w, my, p, b); break;
  4428. case RUBBLE: res= new Rubble( w, my, p, b); break;
  4429. case QUORUM: res= new Quorum( w, my, p, b); break;
  4430. case BOUNCER: res= new Bouncer(w, my, p, b); break;
  4431. case NIMBLER: res= new Nimbler(w, my, p, b); break;
  4432. case KEEP: res= new Keep( w, my, p, b); break;
  4433. }
  4434. res.pieceType = pt;
  4435. return res;
  4436. }
  4437. //--------------------------------------------------------------------
  4438. // General Piece constructor
  4439. //--------------------------------------------------------------------
  4440.  
  4441. Piece(boolean w, boolean my, Board.Position pos, Board board)
  4442. {
  4443. stride = board.SIZE; // set default values
  4444. range = board.SIZE;
  4445. power = 1;
  4446. extent = board.SIZE/2 ;
  4447. moveable = true;
  4448. moveTime = 2;
  4449. buildTime = 6;
  4450.  
  4451. whiteside = w; // set values from params
  4452. this.pos = pos;
  4453. this.board = board;
  4454. myside = my;
  4455.  
  4456. // Set a polygon to approximate a circle
  4457.  
  4458. if (board.shape == 2) nPoly = 6; else nPoly = 28; // Determine polygon degree from shape param
  4459.  
  4460. if (board.shape ==2 | board.shape ==3) // define a regular nPoly-polygon
  4461. {xPoly = new float[nPoly];
  4462. yPoly = new float[nPoly];
  4463. for (int i=0; i<nPoly; i++)
  4464. {xPoly[i] = (float)Math.sin(2*Math.PI*(i+0.5)/nPoly);
  4465. yPoly[i] = (float)Math.cos(2*Math.PI*(i+0.5)/nPoly);
  4466. }
  4467.  
  4468. }
  4469.  
  4470. }
  4471.  
  4472. //----------------------------------------------------------------------------------
  4473. // circArea: generate an area around pos with given radius. if isCirc it is a perfect circle, OW as determined by shape
  4474. //----------------------------------------------------------------------------------
  4475.  
  4476. private Area circArea (int radius, boolean isCirc)
  4477. {
  4478. Shape circ = null;
  4479.  
  4480. // isCirc: generate circle
  4481.  
  4482. if (isCirc) circ = new Ellipse2D.Float((int)(pos.x-radius), (int)(pos.y-radius),
  4483. (int)(2*radius),(int)(2*radius));
  4484.  
  4485. // shape == 1: generate square
  4486.  
  4487. else if (board.shape == 1) circ = new Rectangle((int)(pos.x-radius/Math.sqrt(2)), (int)(pos.y-radius/Math.sqrt(2)),
  4488. (int)(radius*Math.sqrt(2)),(int)(radius*Math.sqrt(2)));
  4489.  
  4490. // OW generate nPoly-polygon
  4491.  
  4492. else if (board.shape == 2 | board.shape==3)
  4493. {
  4494. int [] x = new int [nPoly];
  4495. int [] y = new int [nPoly];
  4496. for (int i=0;i<nPoly;i++)
  4497. {x[i] = (int)(pos.x + radius*xPoly[i]);
  4498. y[i] = (int)(pos.y + radius*yPoly[i]);
  4499. }
  4500. circ = new Polygon(x,y,nPoly);
  4501. }
  4502.  
  4503. return new Area(circ);
  4504. }
  4505.  
  4506. //--------------------------------------------------------------------------------
  4507. // Area functions
  4508. //--------------------------------------------------------------------------------
  4509.  
  4510. // setAreas just defines the Area fields, typically after the piece has moved
  4511.  
  4512. void setAreas()
  4513.  
  4514. { currentRange = (int)( range * board.elevation(pos)); // adjust the range: if on a hill multiply with its height
  4515. rangeArea = circArea(currentRange,false);
  4516. rangeArea.intersect(board.boardArea); // range only on board
  4517. extentArea = circArea(extent, true);
  4518. strideArea = circArea(stride,false);
  4519. avoidBuildArea = circArea(board.SIZE*2/3,true);
  4520. nearArea = circArea(board.SIZE,true);
  4521.  
  4522.  
  4523. }
  4524.  
  4525. // Generate area clones
  4526.  
  4527. Area rangeArea ()
  4528. {return (Area)rangeArea.clone();}
  4529.  
  4530. Area strideArea()
  4531. {return (Area)strideArea.clone();}
  4532.  
  4533. Area nearArea()
  4534. {return (Area)nearArea.clone();}
  4535.  
  4536. // Generate avoidarea: where a piece of type p may not come
  4537.  
  4538. Area avoidArea(Piece p)
  4539. {return circArea(p.extent + extent, true);}
  4540.  
  4541.  
  4542.  
  4543.  
  4544. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4545. //
  4546. // Graphic display methods
  4547. //
  4548. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4549.  
  4550.  
  4551. abstract Area appearance(Board.Position pos); // Each piece must define an appearance
  4552.  
  4553.  
  4554. //---------------------------------------------------------------------
  4555. // Draw an outline of a piece type with specified color at specified position
  4556. //---------------------------------------------------------------------
  4557.  
  4558.  
  4559. void drawOutline(Graphics g, Board.Position pos, Color color)
  4560. {Graphics2D page2 = (Graphics2D)g;
  4561. g.setColor(color);
  4562. if (color == Color.red) page2.setStroke(new BasicStroke(2));
  4563. page2.draw(appearance(pos));
  4564. page2.setStroke(new BasicStroke(1));
  4565. }
  4566.  
  4567.  
  4568.  
  4569. //---------------------------------------------------------------------
  4570. // Draw a piece type with specified color, including a bounding outline of opposite color
  4571. //---------------------------------------------------------------------
  4572.  
  4573. void draw(Graphics g, Board.Position pos, Color color)
  4574. {Graphics2D page2 = (Graphics2D)g;
  4575. g.setColor(color);
  4576. page2.fill(appearance(pos));
  4577. drawOutline(g, pos, flipColor(color));
  4578. }
  4579.  
  4580.  
  4581.  
  4582.  
  4583. //--------------------------------------------------------------
  4584. // overloaded methods for drawing this instance of a piece.
  4585. // only draw it if it is on a visible square or outside the board.
  4586. //--------------------------------------------------------------
  4587.  
  4588. void draw(Graphics g) // Determine the correct color and then draw the piece
  4589.  
  4590. { boolean whitecontrolled = (whiteside == myside) ? board.iControl(this) : board.opponentControls(this);
  4591. boolean blackcontrolled = (whiteside != myside) ? board.iControl(this) : board.opponentControls(this);
  4592.  
  4593. Color color = whiteside ? (whitecontrolled ? Color.white : (blackcontrolled ? WHITE_OPPONENT : WHITE_NEUTRAL))
  4594. : (blackcontrolled ? Color.black : (whitecontrolled ? BLACK_OPPONENT : BLACK_NEUTRAL));
  4595. if (underAttack)
  4596. color = whitecontrolled ? Board.WHITEHIGHLIGHT : Board.BLACKHIGHLIGHT;
  4597. draw(g, pos, color);}
  4598.  
  4599.  
  4600. //-----------------------------------------------------------------
  4601.  
  4602. void draw(Graphics g, Color altcolor) // Draw the piece with a specified color
  4603. {
  4604. draw(g, pos, altcolor);
  4605. }
  4606.  
  4607. void drawOutline(Graphics g, Color altcolor) // Draw an outline with specified color
  4608. {
  4609. drawOutline(g, pos, altcolor);
  4610. }
  4611.  
  4612. //-----------------------------------------------------------------
  4613.  
  4614. private Color flipColor(Color color) // used to determine border of filled piece
  4615. {return (whiteside & !(color == Color.black) ? Color.black : Color.white);}
  4616.  
  4617.  
  4618. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4619. //
  4620. // Other utility methods
  4621. //
  4622. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  4623.  
  4624.  
  4625. //---------------------------------------------------------------------
  4626. // Determine when a selector should be shown (this is refined by subclasses)
  4627. //---------------------------------------------------------------------
  4628.  
  4629. boolean shouldShowSelector ()
  4630. {return (MAXIMUM > board.howManyOnBoard(pieceType) && specificSelector());}
  4631.  
  4632. abstract boolean specificSelector(); // a subclass should define this to refine shouldShowSelector
  4633.  
  4634. //------------------------------------------------------------------------------
  4635. // Default canBuild, determines if a piece of this type can be built at position p.
  4636. // refined by subclasses. side = true means it is me who builds, ow opponent builds
  4637. //-------------------------------------------------------------------------------
  4638.  
  4639. boolean canBuild(Board.Position p)
  4640. { return (board.isLegal(p) // must be on the board
  4641. && board.iControl(p) // on a controlled square
  4642. && MAXIMUM > board.howManyOnBoard(pieceType)); // and not exceeding max allowed of piece type
  4643.  
  4644. }
  4645.  
  4646.  
  4647. //-------------------------------------------------------------------------------
  4648. // Default canMove, determines if this piece can move to p
  4649. //-------------------------------------------------------------------------------
  4650.  
  4651. boolean canMove (Board.Position p)
  4652. { if (p != null && board.canMoveTo(p)) // Can only move to highlighted area
  4653. return !p.equals(pos); // but not to the same position!
  4654. else
  4655. {Piece inhabitant = board.find(p); // or to enemy piece under attack
  4656. return inhabitant != null && inhabitant.myside != myside && inhabitant.underAttack;
  4657. }
  4658.  
  4659.  
  4660. }
  4661.  
  4662. //--------------------------------------------------------------
  4663. // Move the piece to another place on the board
  4664. //--------------------------------------------------------------
  4665.  
  4666. Piece moveTo(Board.Position pos)
  4667. {Piece victim = board.find(pos); // check if there is a victim on the destination position
  4668. if (victim != null) board.removePiece(victim); // if so remove it
  4669. this.pos = pos; // then adjust the position of the piece
  4670. setAreas(); // and recalculate its areas
  4671. return this;
  4672. }
  4673.  
  4674.  
  4675.  
  4676. //--------------------------------------------------------------
  4677. // Check if a particular position is near the piece
  4678. //--------------------------------------------------------------
  4679.  
  4680. boolean near (Board.Position pos)
  4681. {return nearArea.contains(pos.x,pos.y);}
  4682.  
  4683.  
  4684. //--------------------------------------------------------------
  4685. // Check if a particular position is on the piece
  4686. //--------------------------------------------------------------
  4687.  
  4688. boolean on (Board.Position pos) // If the position is closer than the extent of the piece
  4689. {return extentArea.contains(pos.x,pos.y);}
  4690.  
  4691. } // End class Piece
  4692.  
  4693.  
  4694. //********************************************************************
  4695. //
  4696. // Pebble
  4697. //
  4698. //********************************************************************
  4699.  
  4700. class Pebble extends Piece
  4701. {
  4702. final private int OSIZE = board.SIZE/3; // Size of the Pebble
  4703.  
  4704. final private Keep myKeep = new Keep(true, true, null, board); // A Keep, just to be able to show an outline when promoting a rubble
  4705.  
  4706. //-------------------------------------------------------------------
  4707. // Construct a pebble: construct a Piece, set its type to pebble, and decrease remaining pebbles
  4708. //-------------------------------------------------------------------
  4709.  
  4710. Pebble(boolean w, boolean my, Board.Position pos, Board board)
  4711. {
  4712. super(w,my,pos,board);
  4713. MAXIMUM=8;
  4714. extent = OSIZE/2;
  4715. if (board.isLegal(pos)) board.decreaseRemaining(w); // decrease remaining pebbles if on the board
  4716. } // (ow it is a selector)
  4717.  
  4718.  
  4719. //---------------------------------------------------------------------
  4720. // Appearance
  4721. //---------------------------------------------------------------------
  4722.  
  4723. Area appearance(Board.Position pos) // Appearance is just a circle
  4724. {return new Area(new Ellipse2D.Float(pos.x-OSIZE/2,
  4725. pos.y-OSIZE/2,
  4726. OSIZE,
  4727. OSIZE));}
  4728.  
  4729.  
  4730.  
  4731. //---------------------------------------------------------------------
  4732. // Determine conditions for showing selector pebble
  4733. //---------------------------------------------------------------------
  4734.  
  4735. boolean specificSelector()
  4736.  
  4737. {return board.howManyOnBoard(Type.RUBBLE) > 0 // A rubble on the board
  4738. && board.remaining(board.iPlayWhite) > 0; // and pebbles remaining to put into play
  4739.  
  4740. }
  4741.  
  4742.  
  4743. //---------------------------------------------------------------------
  4744. // Determine conditions for building a pebble
  4745. //---------------------------------------------------------------------
  4746. @Override
  4747. boolean canBuild(Board.Position p)
  4748. {
  4749. return board.canBuildPebbleOn(p);} // Builds possible on buildable area
  4750.  
  4751.  
  4752. //----------------------------------------------------------------------
  4753. // Effects of moving a pebble (overrides Piece.moveTo because this can create a keep
  4754. //----------------------------------------------------------------------
  4755.  
  4756. @Override
  4757. Piece moveTo(Board.Position pos)
  4758. {Piece dest = board.find(pos);
  4759. if (dest == null || dest.pieceType != Type.RUBBLE || dest.myside != myside) // An ordinary move
  4760. return super.moveTo(pos); // is just as done by super
  4761. else {board.removePiece(dest); // but a keep-building move
  4762. board.removePiece(this); // means removing both pebble and rubble
  4763. Piece res = board.newPiece(Type.KEEP,whiteside,myside,pos); // and inserting a new keep
  4764. moveTime = res.buildTime; // and this takes as long as to build a keep!
  4765. return res; // (no need to reset moveTimet since this pebble disappears)
  4766. }
  4767. }
  4768.  
  4769.  
  4770.  
  4771. @Override
  4772. boolean canMove(Board.Position p)
  4773. {outline = this; // the outline is only changed by canKeep
  4774. return super.canMove(p) || canKeep(p);} // Either an ordinary move or a keep build
  4775.  
  4776.  
  4777. //---------------------------------------------------------------------
  4778. // Determine conditions for building a keep out of a pebble
  4779. //---------------------------------------------------------------------
  4780.  
  4781. boolean canKeep(Board.Position p)
  4782. {
  4783. if (board.isLite) return false; // In a lite game you can never build a Keep
  4784. else {Piece inhabitant = board.find(p);
  4785. boolean res = // Ow you can construct a Keep
  4786. (
  4787. board.isLegal(p) // to a place on the board
  4788. && inhabitant != null // which is inhabited
  4789. && inhabitant.pieceType == Type.RUBBLE // by a rubble
  4790. && inhabitant.myside == myside // on our side
  4791. && strideArea.contains(p.x,p.y) // not too far away
  4792. && board.iControl(p) // destination rubble must be controlled
  4793. && myKeep.MAXIMUM > board.howManyOnBoard(Type.KEEP) // not too many keeps already
  4794. );
  4795. if (res) outline = myKeep; // change outline if I can build a keep
  4796. return res;
  4797. }
  4798. }
  4799.  
  4800. } // End class Pebble
  4801.  
  4802.  
  4803. //*****************************************************************
  4804. //
  4805. // Rubble
  4806. //
  4807. //*****************************************************************
  4808.  
  4809. class Rubble extends Piece
  4810. {
  4811.  
  4812. final private int RSIZE = (board.SIZE * 25)/60; // graphical size
  4813.  
  4814. //--------------------------------------------------------------------
  4815. // Rubble constructor
  4816. //--------------------------------------------------------------------
  4817.  
  4818. Rubble (boolean w, boolean my, Board.Position pos, Board board)
  4819. {
  4820. super(w,my,pos, board); // Construct a piece
  4821. range = (int)board.SIZE/3; // with very small range
  4822. moveable = false; // not moveable
  4823. stride = 0;
  4824. MAXIMUM = 2; // max 2 allowed on board
  4825. extent = board.SIZE/3; // large extent because this may become a Keep
  4826.  
  4827. }
  4828.  
  4829. //---------------------------------------------------------------------
  4830. // Appearance
  4831. //---------------------------------------------------------------------
  4832.  
  4833. Area appearance(Board.Position pos) // Appearance is a square
  4834. {return new Area(new Rectangle2D.Float(pos.x-RSIZE/2,
  4835. pos.y-RSIZE/2,
  4836. RSIZE,
  4837. RSIZE));}
  4838.  
  4839.  
  4840.  
  4841.  
  4842. //---------------------------------------------------------------------
  4843. // Determine when selector should be shown
  4844. //---------------------------------------------------------------------
  4845.  
  4846. boolean specificSelector()
  4847.  
  4848. {return board.howManyOnBoard(Type.PEBBLE) > 0;} // Pebbles on the board
  4849.  
  4850. //---------------------------------------------------------------------
  4851. // Determine when rubble can be built
  4852. //---------------------------------------------------------------------
  4853.  
  4854.  
  4855. @Override
  4856. boolean canBuild(Board.Position p)
  4857. {
  4858. boolean res;
  4859. res = super.canBuild(p) // Can build piece
  4860. && board.near(Type.PEBBLE, p); // and near a pebble
  4861. return res;
  4862. }
  4863. } // End class Rubble
  4864.  
  4865.  
  4866.  
  4867. //*******************************************************************
  4868. //
  4869. // Keep
  4870. //
  4871. //*******************************************************************
  4872.  
  4873. class Keep extends Piece
  4874. {
  4875. final private int BSIZE = board.SIZE;
  4876. final private int KM = (BSIZE * 10) / 60; // sizing constants for graphics
  4877. final private int KW = (BSIZE * 15) / 60;
  4878.  
  4879.  
  4880. // the keep polygon shape
  4881.  
  4882. final private int[] Kx = {KM, BSIZE/2-KW/2, BSIZE/2-KW/2, BSIZE/2+KW/2, BSIZE/2+KW/2, BSIZE-KM,
  4883. BSIZE-KM, BSIZE/2+KW/2, BSIZE/2+KW/2, BSIZE/2-KW/2, BSIZE/2-KW/2, KM};
  4884. final private int[] Ky = {BSIZE/2-KW/2, BSIZE/2-KW/2, KM, KM, BSIZE/2-KW/2, BSIZE/2-KW/2,
  4885. BSIZE/2+KW/2, BSIZE/2+KW/2, BSIZE-KM, BSIZE-KM,BSIZE/2+KW/2, BSIZE/2+KW/2};
  4886. final private int Klength = 12;
  4887.  
  4888. private int kx[] = new int[12]; // temps when drawing
  4889. private int ky[] = new int[12];
  4890.  
  4891.  
  4892. //--------------------------------------------------------------------
  4893. // Keep constructor
  4894. //--------------------------------------------------------------------
  4895.  
  4896. Keep(boolean w, boolean my, Board.Position pos, Board board)
  4897. {
  4898. super(w,my,pos,board); // construct a piece
  4899. moveable = false; // not moveable
  4900. stride = 0;
  4901. extent = board.SIZE/3;
  4902. MAXIMUM = 2;
  4903. moveTime = 8; // delay of a build (because pebble creates a keep through a move)
  4904. }
  4905.  
  4906. //---------------------------------------------------------------------
  4907. // Appearance
  4908. //---------------------------------------------------------------------
  4909.  
  4910. Area appearance(Board.Position pos)
  4911. {for (int i=0; i<Klength; i++)
  4912. {kx[i] = Kx[i] + pos.x - BSIZE/2;
  4913. ky[i] =Ky[i] + pos.y - BSIZE/2;}
  4914. return new Area (new Polygon(kx,ky, Klength));
  4915. }
  4916.  
  4917.  
  4918. //---------------------------------------------------------------------
  4919. // Never show selector
  4920. //---------------------------------------------------------------------
  4921.  
  4922. boolean specificSelector()
  4923. {return false;
  4924. }
  4925.  
  4926. //---------------------------------------------------------------------
  4927. // Never build it (because it is created by a pebble move)
  4928. //---------------------------------------------------------------------
  4929.  
  4930. @Override
  4931. boolean canBuild(Board.Position p)
  4932. { return false;
  4933. }
  4934.  
  4935. } // End class Keep
  4936.  
  4937.  
  4938.  
  4939. //*******************************************************************
  4940. //
  4941. // Heavy Piece: What is common for Quorum, Bouncer and Nimbler
  4942. //
  4943. //*******************************************************************
  4944.  
  4945. abstract class HeavyPiece extends Piece
  4946. {
  4947.  
  4948. HeavyPiece(boolean w, boolean my, Board.Position pos, Board board)
  4949. {super(w,my,pos,board); // construct a piece
  4950. MAXIMUM =2; // Heavy pieces have maximum = 2
  4951.  
  4952. }
  4953.  
  4954. //---------------------------------------------------------------------
  4955. // Determine when heavy piece selector should be shown
  4956. //---------------------------------------------------------------------
  4957.  
  4958. boolean specificSelector()
  4959. {return board.howManyOnBoard(Type.KEEP) > 0 && board.howManyOnBoard(Type.PEBBLE) > 0;} // same for all heavy pieces
  4960.  
  4961.  
  4962. //---------------------------------------------------------------------
  4963. // Determine when heavy piece can be built
  4964. //---------------------------------------------------------------------
  4965.  
  4966. @Override
  4967. boolean canBuild(Board.Position pos)
  4968. { boolean res;
  4969. Piece inhabitant = board.find(pos);
  4970. res = board.near(Type.KEEP, pos) // near a keep
  4971. && inhabitant != null // on a pebble
  4972. && inhabitant.pieceType == Type.PEBBLE;
  4973. return super.canBuild(pos) && res;
  4974. }
  4975. }
  4976.  
  4977.  
  4978. //*******************************************************************
  4979. //
  4980. // Bouncer
  4981. //
  4982. //*******************************************************************
  4983.  
  4984. class Bouncer extends HeavyPiece
  4985. {
  4986. final private int OSIZE = board.SIZE/3; // various constants for the graphic representation, in pixels
  4987. final private int BWIDTH =(8 * board.SIZE) / 60 ;
  4988. final private int BSIZE = board.SIZE*3/4;
  4989.  
  4990.  
  4991.  
  4992.  
  4993. //--------------------------------------------------------------------
  4994. // Bouncer constructor
  4995. //--------------------------------------------------------------------
  4996.  
  4997. Bouncer(boolean w, boolean my, Board.Position pos, Board board)
  4998. {
  4999. super(w,my,pos,board); // construct a piece
  5000. range =2*board.SIZE; // with range 2
  5001. extent = board.SIZE/3; // The bouncer is large!
  5002. }
  5003.  
  5004. //---------------------------------------------------------------------
  5005. // Appearance
  5006. //---------------------------------------------------------------------
  5007.  
  5008. Area appearance(Board.Position pos)
  5009. { Area app =
  5010. new Area(new Ellipse2D.Float(pos.x-OSIZE/2, // Appearance is a circle plus two polygons
  5011. pos.y-OSIZE/2,
  5012. OSIZE,
  5013. OSIZE));
  5014. int[] Bx1 = {pos.x-BSIZE/2, pos.x+BSIZE/2 - BWIDTH/2, pos.x+BSIZE/2, pos.x-BSIZE/2 + BWIDTH/2};
  5015. int[] By1 = {pos.y-BSIZE/2 + BWIDTH/2, pos.y+BSIZE/2, pos.y+BSIZE/2 - BWIDTH/2, pos.y-BSIZE/2};
  5016. app.add(new Area (new Polygon(Bx1,By1,4)));
  5017. int [] Bx2 = {pos.x+BSIZE/2 - BWIDTH/2, pos.x-BSIZE/2, pos.x-BSIZE/2 + BWIDTH/2, pos.x+BSIZE/2};
  5018. int [] By2 = {pos.y-BSIZE/2, pos.y+BSIZE/2 - BWIDTH/2, pos.y+BSIZE/2, pos.y-BSIZE/2 + BWIDTH/2};
  5019. app.add(new Area (new Polygon(Bx2,By2,4)));
  5020. return app;
  5021. }
  5022.  
  5023. } // End class Bouncer
  5024.  
  5025.  
  5026.  
  5027. //*******************************************************************
  5028. //
  5029. // Quorum
  5030. //
  5031. //*******************************************************************
  5032.  
  5033. class Quorum extends HeavyPiece {
  5034.  
  5035. final private int OSIZE = board.SIZE / 3; // size of graphic representation, in pixels
  5036.  
  5037. //--------------------------------------------------------------------
  5038. // Quorum constructor
  5039. //--------------------------------------------------------------------
  5040.  
  5041. Quorum(boolean w, boolean my, Board.Position pos, Board board)
  5042. {
  5043. super(w,my,pos,board); // construct a piece
  5044. power =2; // with power 2
  5045. extent = 3*OSIZE/4;
  5046. }
  5047.  
  5048. //---------------------------------------------------------------------
  5049. // Appearance
  5050. //---------------------------------------------------------------------
  5051.  
  5052. Area appearance(Board.Position pos) // Appearance is two overlapping circles
  5053. {Area app = new Area(new Ellipse2D.Float(pos.x-OSIZE/2,
  5054. pos.y-OSIZE/2+OSIZE/4,
  5055. OSIZE,
  5056. OSIZE));
  5057. app.add(new Area(new Ellipse2D.Float(pos.x-OSIZE/2,
  5058. pos.y-OSIZE/2-OSIZE/4,
  5059. OSIZE,
  5060. OSIZE)));
  5061. return app;}
  5062.  
  5063. //---------------------------------------------------------------------
  5064. // drawOutline (overrides Piece.drawOutline in order to draw an extra circle
  5065. //---------------------------------------------------------------------
  5066.  
  5067. @Override
  5068. protected void drawOutline(Graphics page, Board.Position pos, Color color)
  5069. { super.drawOutline(page, pos, color);
  5070. page.drawOval(pos.x-OSIZE/2,
  5071. pos.y-OSIZE/2+OSIZE/4,
  5072. OSIZE,
  5073. OSIZE);
  5074. }
  5075.  
  5076. } // End class Quorum
  5077.  
  5078. //*******************************************************************
  5079. //
  5080. // Nimbler
  5081. //
  5082. //*******************************************************************
  5083.  
  5084. class Nimbler extends HeavyPiece
  5085. {
  5086.  
  5087. final private int NxSIZE = (board.SIZE * 5)/60; // graphic size
  5088. final private int NySIZE = (board.SIZE * 20)/60;
  5089. final private int OSIZE = (board.SIZE * 15)/60;
  5090.  
  5091. final private int[] Nx = {0, NxSIZE, -NxSIZE, 0, NxSIZE, -NxSIZE}; // Nimbler polygon shape
  5092. final private int[] Ny = {0, -NySIZE, -NySIZE, 0, NySIZE, NySIZE};
  5093.  
  5094. private int[] Nxx = new int[6]; // temps
  5095. private int[] Nyy = new int[6];
  5096.  
  5097.  
  5098.  
  5099.  
  5100. //--------------------------------------------------------------------
  5101. // Nimbler constructor
  5102. //--------------------------------------------------------------------
  5103.  
  5104. Nimbler(boolean w, boolean my, Board.Position pos, Board board)
  5105. {
  5106. super(w,my,pos, board); // construct a piece
  5107. stride = board.SIZE*2; // with stride 2
  5108. moveTime = 1; // with a short move time
  5109. extent = NySIZE;
  5110. }
  5111.  
  5112. //---------------------------------------------------------------------
  5113. // Appearance
  5114. //---------------------------------------------------------------------
  5115. Area appearance(Board.Position pos)
  5116. {Area app = new Area(new Ellipse2D.Float(pos.x-OSIZE/2,
  5117. pos.y-OSIZE/2,
  5118. OSIZE,
  5119. OSIZE));
  5120. for (int i=0; i<=5; i++) {Nxx[i] = Nx[i] + pos.x; Nyy[i] = Ny[i] + pos.y;}
  5121. app.add (new Area(new Polygon(Nxx, Nyy, 6)));
  5122. return app;
  5123.  
  5124. }
  5125.  
  5126.  
  5127.  
  5128. } // End class Nimbler
  5129.  
  5130.  
  5131. // End of file
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement