/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * AndroidWorld Library, Copyright 2011 Bryan Chadwick * * * * FILE: ./android/world/test/ConnectFourGame.java * * * * This file is part of AndroidWorld. * * * * AndroidWorld is free software: you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation, either version * * 3 of the License, or (at your option) any later version. * * * * AndroidWorld is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with AndroidWorld. If not, see <http://www.gnu.org/licenses/>. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package android.world.test; import java.util.*; import android.os.Bundle; import android.view.Display; import android.world.*; import android.app.Activity; import android.image.*; public class ConnectFourGame extends Activity{ public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); Display dis = getWindowManager().getDefaultDisplay(); new ConnectFour(dis.getWidth(), dis.getHeight()-50).bigBangLandscape(this); } } /** Represents the State of the game * - This is an alternative to the other examples, where we * track the state, and react to *it*, rather than having * the state classes encapsulate the reactions themselves. */ abstract class State{ /** What happens when someone wins */ State win(){ return this; } /** What happens when the game is restarted */ State restart(ConnectFour game){ return this; } /** Are we in the "Playing" state? (not the Losing State) */ boolean isPlaying(){ return false; } } /** Represents the normal game play State */ class Play extends State{ /** When someone wins, we enter the Lose State */ State win(){ return new Lose(); } /** Yes... we are Playing */ boolean isPlaying(){ return true; } } /** */ class Lose extends State{ /** When we restart, we restart and enter the Play State */ State restart(ConnectFour game){ game.restart(); return new Play(); } } /** Represents a Player's Token */ class Token{ String color; Token(String color) { this.color = color; } } /** Represents the <blink>Connect Four</blink> <marquee>Game / World</marquee> */ class ConnectFour extends VoidWorld{ static int G_WIDTH = 7; static int G_HEIGHT = 6; State state; /** Convert a grid number to screen X */ int grid2ScreenX(int gx){ return gx*this.SIZE+this.SIZE/2+(this.S_WIDTH-(G_WIDTH*this.SIZE))/2; } /** Convert a grid number to screen Y */ int grid2ScreenY(int gy){ return this.S_HEIGHT-(this.S_HEIGHT-(G_HEIGHT*this.SIZE))/2- (gy*this.SIZE+this.SIZE/2); } /** Screen/Scene Size and the width of a Grid/Piece * I changed them to be fields (i.e., not static) so that we can * move the game to Android easily. */ int S_WIDTH, S_HEIGHT; int SIZE; /** Basic Background... */ Scene BACK; boolean who = true; /** One possible representation of the current state of our game */ ConnectRep chips; ConnectFour(int W, int H){ this(W, H,new Board(ConnectFour.G_WIDTH)); } ConnectFour(int W, int H, ConnectRep chips){ this.S_WIDTH = W; this.S_HEIGHT = H; this.SIZE = Math.min(W, H)/Math.max(G_WIDTH, G_HEIGHT); this.chips = chips; this.BACK = new EmptyScene(this.S_WIDTH, this.S_HEIGHT) .placeImage(new Rectangle(this.S_WIDTH, this.S_HEIGHT, "solid", "gold"), this.S_WIDTH/2, this.S_HEIGHT/2) .placeImage(new Rectangle(this.S_WIDTH, this.SIZE/8, "solid", "blue"), this.S_WIDTH/2, this.S_HEIGHT-this.SIZE/16); this.state = new Play(); } /** Slow the tick rate to enhance overall performance */ public double tickRate(){ return 1.0; } /** NEW: Restart the game */ void restart(){ this.chips = new Board(ConnectFour.G_WIDTH); this.who = true; } /** Add a Chip on Mouse Click */ public void onMouse(int x, int y, String me){ // NEW: playing determines action... if(this.state.isPlaying() && me.equals("button-down")){ this.chips.addToken((x-(this.S_WIDTH-(G_WIDTH*this.SIZE))/2)/this.SIZE, currentPlayer()); if(checkWin(currentPlayer())) this.state = this.state.win(); this.who = !this.who; }else{ // NEW: Get ready for Android!!! Restart the game on a long press if(me.equals("long-button-down")) this.state = this.state.restart(this); } } public void onKey(String key){ // NEW: playing determines action... if(!this.state.isPlaying()){ if(key.equals("\n")) this.state = this.state.restart(this); } } /** Draw all the tokens in the board */ public Scene onDraw(){ // NEW: playing determines action... if(this.state.isPlaying()) return drawchips(0, 0, this.BACK); else return lastScene(); } /** Draw everything */ Scene drawchips(int gx, int gy, Scene scn){ if(gy >= G_HEIGHT){ return scn; }else{ if(gx >= G_WIDTH){ return drawchips(0, gy+1, scn); }else{ return drawchips(gx+1, gy, scn.placeImage(new Circle(this.SIZE/2-this.SIZE/8, "solid", this.chips.getOwner(gx, gy)), this.grid2ScreenX(gx), this.grid2ScreenY(gy))); } } } /** See if the player won */ boolean checkWin(String player){ for(int row = 0; row < G_HEIGHT; row++){ for(int col = 0; col < G_WIDTH; col++){ if(this.chips.getOwner(col, row).equals(player) && (sameStrs(col, row, col, row+1, col, row+2, col, row+3) || sameStrs(col, row, col+1, row, col+2, row, col+3, row) || sameStrs(col, row, col+1, row+1, col+2, row+2, col+3, row+3) || sameStrs(col, row, col+1, row-1, col+2, row-2, col+3, row-3))){ return true; } } } return false; } /** Are all the "owners" of the indices the same */ boolean sameStrs(int c1, int r1, int c2, int r2, int c3, int r3, int c4, int r4){ return (this.chips.getOwner(c1, r1).equals(this.chips.getOwner(c2, r2)) && this.chips.getOwner(c2, r2).equals(this.chips.getOwner(c3, r3)) && this.chips.getOwner(c3, r3).equals(this.chips.getOwner(c4, r4))); } /** Who's the player waiting to play */ String currentPlayer(){ if(this.who)return "red"; return "black"; } /** Who's the player who just went */ String previousPlayer(){ if(this.who)return "black"; return "red"; } /** Draw an exciting message at the end of the game!! */ public Scene lastScene(){ return this.addMessage("You Won "+ this.previousPlayer().toUpperCase(), this.S_WIDTH/2+2, this.S_HEIGHT/2+2, this.previousPlayer(), this.addMessage("Press ENTER to play again!", this.S_WIDTH/2, this.S_HEIGHT/2+40, this.previousPlayer(), this.drawchips(0, 0, this.BACK))); } /** Add a message, so that we can make it readable. */ public Scene addMessage(String s, int x, int y, String color, Scene scn){ return scn.placeImage(new Text(s, this.S_WIDTH/20, "darkgray"), x+2, y+2) .placeImage(new Text(s, this.S_WIDTH/20, color), x, y); } } /** Represents a Connect Four Board */ interface ConnectRep{ /** Get the owner of the given square/space/spot */ String getOwner(int col, int row); /** Add a token to the given column with the given Owner */ void addToken(int col, String owner); } /** Johns representation of the Connect Four Board */ class Board implements ConnectRep{ ArrayList<ArrayList<Token>> board; Board(int cols){ // Need ArrayList<...> this.board = new ArrayList<ArrayList<Token>>(); this.loopItUp(cols, this.board); } /** Add all the necessary columns to the ArrayList */ void loopItUp(int c, ArrayList<ArrayList<Token>> lst){ if(c > 0){ lst.add(new ArrayList<Token>()); this.loopItUp(c-1, lst); } } /** Get the owner of the given square/space/spot */ public String getOwner(int col, int row){ if(this.board.size() > col && col >= 0 && this.board.get(col).size() > row && row >= 0){ // Has Owner return this.board.get(col).get(row).color; }else{ // No Owner return "white"; } } /** Add a token to the given column with the given Owner */ public void addToken(int col, String owner){ if(this.board.get(col).size() < ConnectFour.G_HEIGHT){ // Not Full... this.board.get(col).add(new Token(owner)); } } }