/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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 */
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> board;
Board(int cols){
// Need ArrayList<...>
this.board = new ArrayList>();
this.loopItUp(cols, this.board);
}
/** Add all the necessary columns to the ArrayList */
void loopItUp(int c, ArrayList> lst){
if(c > 0){
lst.add(new ArrayList());
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));
}
}
}