diff --git a/app/build.gradle b/app/build.gradle index a096ef1..33b4d16 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 24 + buildToolsVersion "24.0.0" defaultConfig { applicationId "com.zduo.dotsandboxes" minSdkVersion 14 - targetSdkVersion 23 + targetSdkVersion 24 versionCode 1 versionName "1.0" } @@ -21,6 +21,8 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) + + compile 'com.android.support:appcompat-v7:24.0.0' + testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.4.0' } diff --git a/app/src/main/java/com/zduo/dotsandboxes/MainActivity.java b/app/src/main/java/com/zduo/dotsandboxes/MainActivity.java index f8ce439..05c7e17 100644 --- a/app/src/main/java/com/zduo/dotsandboxes/MainActivity.java +++ b/app/src/main/java/com/zduo/dotsandboxes/MainActivity.java @@ -7,6 +7,7 @@ import android.widget.ImageView; import android.widget.TextView; +import com.zduo.dotsandboxes.ai.RandomAIPlayer; import com.zduo.dotsandboxes.model.HumanPlayer; import com.zduo.dotsandboxes.model.Player; import com.zduo.dotsandboxes.view.GameView; @@ -37,16 +38,11 @@ protected void onCreate(Bundle savedInstanceState) { player2occupying = (TextView) findViewById(R.id.player2occupying); currentPlayerPointer = (ImageView) findViewById(R.id.playerNowPointer); - new Thread() { - @Override - public void run() { - startGame(); - } - }.start(); + startGame(); } private void startGame() { - players = new Player[]{new HumanPlayer("Player1"), new HumanPlayer("Player2")}; + players = new Player[]{new HumanPlayer("Human"), new RandomAIPlayer("Computer")}; gameView.startGame(players); updateState(); } @@ -59,7 +55,7 @@ public void run() { player1state.setText("Thinking"); player2state.setText("Waiting"); currentPlayerPointer.setImageResource(R.drawable.a1); - } else { + } else if (currentPlayer == players[1]) { player2state.setText("Thinking"); player1state.setText("Waiting"); currentPlayerPointer.setImageResource(R.drawable.a2); @@ -91,12 +87,16 @@ public void run() { new AlertDialog.Builder(MainActivity.this) .setTitle("Dots And Boxes") .setMessage(winner.getName() + " Wins!") - .setPositiveButton("Restart", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - recreate(); - } - }).show(); + .setPositiveButton("Restart", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + recreate(); + } + }) + .setNeutralButton("Dismiss", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + } + }).show(); } }); } diff --git a/app/src/main/java/com/zduo/dotsandboxes/ai/Box.java b/app/src/main/java/com/zduo/dotsandboxes/ai/Box.java new file mode 100644 index 0000000..b56b772 --- /dev/null +++ b/app/src/main/java/com/zduo/dotsandboxes/ai/Box.java @@ -0,0 +1,30 @@ +package com.zduo.dotsandboxes.ai; + +public class Box { + boolean left; + boolean top; + boolean right; + boolean bottom; + boolean occupied; + + Box(boolean l, boolean t, boolean r, boolean b) { + this.left = l; + this.top = t; + this.right = r; + this.bottom = b; + + this.occupied = (l && t && r && b); + } + + int occupiedLineCount() { + int count = 0; + + if (this.left) count++; + if (this.right) count++; + if (this.top) count++; + if (this.bottom) + count++; + + return count; + } +} diff --git a/app/src/main/java/com/zduo/dotsandboxes/ai/RandomAIPlayer.java b/app/src/main/java/com/zduo/dotsandboxes/ai/RandomAIPlayer.java new file mode 100644 index 0000000..8c3fce9 --- /dev/null +++ b/app/src/main/java/com/zduo/dotsandboxes/ai/RandomAIPlayer.java @@ -0,0 +1,136 @@ +package com.zduo.dotsandboxes.ai; + +import com.zduo.dotsandboxes.model.Direction; +import com.zduo.dotsandboxes.model.Line; +import com.zduo.dotsandboxes.model.Player; + +import java.util.ArrayList; +import java.util.List; + +public class RandomAIPlayer extends Player { + + protected final ArrayList safeLines; + protected final ArrayList goodLines; + protected final ArrayList badLines; + + public RandomAIPlayer(String name) { + super(name); + + safeLines = new ArrayList<>(); + goodLines = new ArrayList<>(); + badLines = new ArrayList<>(); + } + + protected Line nextMove() { + if (goodLines.size() != 0) return getBestGoodLine(); + if (safeLines.size() != 0) return getRandomSafeLine(); + + return getRandomBadLine(); + } + + public Line move() { + initialiseLines(); + return nextMove(); + } + + private void initialiseLines() { + goodLines.clear(); + badLines.clear(); + safeLines.clear(); + + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 5; j++) { + if (!isHorizontalLineOccupied(i, j)) { + if (i == 0) { + switch (getBox(i, j).occupiedLineCount()) { + case 3: + goodLines.add(new Line(Direction.HORIZONTAL, i, j)); + break; + case 2: + badLines.add(new Line(Direction.HORIZONTAL, i, j)); + break; + case 1: + case 0: + safeLines.add(new Line(Direction.HORIZONTAL, i, j)); + } + } else if (i == 5) { + switch (getBox(i - 1, j).occupiedLineCount()) { + case 3: + goodLines.add(new Line(Direction.HORIZONTAL, i, j)); + break; + case 2: + badLines.add(new Line(Direction.HORIZONTAL, i, j)); + break; + case 1: + case 0: + safeLines.add(new Line(Direction.HORIZONTAL, i, j)); + } + } else { + if (getBox(i, j).occupiedLineCount() == 3 + || getBox(i - 1, j).occupiedLineCount() == 3) + goodLines.add(new Line(Direction.HORIZONTAL, i, j)); + + if (getBox(i, j).occupiedLineCount() == 2 + || getBox(i - 1, j).occupiedLineCount() == 2) + badLines.add(new Line(Direction.HORIZONTAL, i, j)); + + if (getBox(i, j).occupiedLineCount() < 2 + && getBox(i - 1, j).occupiedLineCount() < 2) + safeLines.add(new Line(Direction.HORIZONTAL, i, j)); + } + } + + if (!isVerticalLineOccupied(j, i)) { + if (i == 0) { + if (getBox(j, i).occupiedLineCount() == 3) + goodLines.add(new Line(Direction.VERTICAL, j, i)); + } else if (i == 5) { + if (getBox(j, i - 1).occupiedLineCount() == 3) + goodLines.add(new Line(Direction.VERTICAL, j, i)); + } else { + if (getBox(j, i).occupiedLineCount() == 3 + || getBox(j, i - 1).occupiedLineCount() == 3) + goodLines.add(new Line(Direction.VERTICAL, j, i)); + + if (getBox(j, i).occupiedLineCount() == 2 + || getBox(j, i - 1).occupiedLineCount() == 2) + badLines.add(new Line(Direction.VERTICAL, j, i)); + + if (getBox(j, i).occupiedLineCount() < 2 + && getBox(j, i - 1).occupiedLineCount() < 2) + safeLines.add(new Line(Direction.VERTICAL, j, i)); + } + } + } + } + } + + protected Box getBox(int row, int column) { + return new Box(isVerticalLineOccupied(row, column), isHorizontalLineOccupied(row, column), + isVerticalLineOccupied(row, column + 1), isHorizontalLineOccupied(row + 1, column)); + } + + protected boolean isHorizontalLineOccupied(int row, int column) { + return getGame().isLineOccupied(Direction.HORIZONTAL, row, column); + } + + protected boolean isVerticalLineOccupied(int row, int column) { + return getGame().isLineOccupied(Direction.VERTICAL, row, column); + } + + protected Line getBestGoodLine() { + return goodLines.get(0); + } + + protected Line getRandomSafeLine() { + return getRandomLine(safeLines); + } + + protected Line getRandomBadLine() { + return getRandomLine(badLines); + } + + private Line getRandomLine(List list) { + return list.get((int) (list.size() * Math.random())); + } +} diff --git a/app/src/main/java/com/zduo/dotsandboxes/model/Player.java b/app/src/main/java/com/zduo/dotsandboxes/model/Player.java index d1fc956..9478bfe 100644 --- a/app/src/main/java/com/zduo/dotsandboxes/model/Player.java +++ b/app/src/main/java/com/zduo/dotsandboxes/model/Player.java @@ -18,6 +18,10 @@ public static int indexIn(Player player, Player[] players) { public abstract Line move(); + public Game getGame() { + return game; + } + public void addToGame(Game game) { this.game = game; } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index be6ae54..5318482 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -26,21 +26,24 @@ android:id="@+id/player1name" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Player 1" /> + android:text="Human" + android:textColor="@color/colorPrimary" /> + android:text="Waiting" + android:textColor="@color/colorPrimary" /> + android:text="Occupying 0" + android:textColor="@color/colorPrimary" /> + android:text="Computer" + android:textColor="@color/colorAccent" /> + android:text="Waiting" + android:textColor="@color/colorAccent" /> + android:text="Occupying 0" + android:textColor="@color/colorAccent" />