├── .gitignore ├── README.md ├── app ├── build.gradle ├── manifest-merger-release-report.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── savva │ │ ├── TicaTacToe │ │ ├── Game.java │ │ ├── GameView.java │ │ └── TicTacToe.java │ │ └── strategies │ │ ├── Board.java │ │ ├── BoardDumb.java │ │ ├── BoardHard.java │ │ └── BoardInterface.java │ └── res │ ├── drawable │ ├── circle.png │ ├── cross.png │ ├── grid.png │ └── icon.png │ ├── layout │ ├── gamefield.xml │ └── main.xml │ └── values │ ├── arrays.xml │ └── strings.xml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Maven 26 | target 27 | 28 | # VM 29 | .vagrant 30 | .user_vagrant.rb 31 | 32 | # OS 33 | .DS_Store 34 | ._* 35 | 36 | # Logs 37 | *.log 38 | 39 | # IntelliJ IDEA 40 | *.iml 41 | *.ipr 42 | *.iws 43 | .idea 44 | 45 | # Eclipse 46 | bin 47 | .classpath 48 | .settings/ 49 | .loadpath 50 | .externalToolBuilders/ 51 | *.launch 52 | .buildpath 53 | *.project 54 | .checkstyle 55 | 56 | # PMD 57 | .pmd 58 | .pmdruleset 59 | 60 | # Netbeans 61 | nbproject/private/ 62 | nbbuild/ 63 | dist/ 64 | nbdist/ 65 | nbactions.xml 66 | nb-configuration.xml 67 | 68 | # Derby 69 | devBootcampDS/ 70 | derby.log 71 | 72 | # Hector 73 | tmp/ 74 | 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CatDogToe 2 | A version of my tic tac toe game for android. 3 | 4 | Please look for details 5 | https://savvavy.wordpress.com/2015/02/01/how-to-beat-medium-cat-dog-toe/ 6 | 7 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 17 5 | buildToolsVersion "21.1.2" 6 | 7 | defaultConfig { 8 | applicationId "com.savva.TicaTacToe" 9 | } 10 | 11 | buildTypes { 12 | release { 13 | minifyEnabled false 14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/manifest-merger-release-report.txt: -------------------------------------------------------------------------------- 1 | -- Merging decision tree log --- 2 | manifest 3 | ADDED from AndroidManifest.xml:2:1 4 | xmlns:android 5 | ADDED from AndroidManifest.xml:2:11 6 | package 7 | ADDED from AndroidManifest.xml:3:5 8 | INJECTED from AndroidManifest.xml:0:0 9 | INJECTED from AndroidManifest.xml:0:0 10 | android:versionName 11 | ADDED from AndroidManifest.xml:5:5 12 | android:versionCode 13 | ADDED from AndroidManifest.xml:4:5 14 | INJECTED from AndroidManifest.xml:0:0 15 | INJECTED from AndroidManifest.xml:0:0 16 | application 17 | ADDED from AndroidManifest.xml:7:5 18 | android:label 19 | ADDED from AndroidManifest.xml:9:9 20 | android:icon 21 | ADDED from AndroidManifest.xml:8:9 22 | activity#com.savva.TicaTacToe.TicTacToe 23 | ADDED from AndroidManifest.xml:10:9 24 | android:label 25 | ADDED from AndroidManifest.xml:12:13 26 | android:name 27 | ADDED from AndroidManifest.xml:11:13 28 | intent-filter#android.intent.action.MAIN+android.intent.category.LAUNCHER 29 | ADDED from AndroidManifest.xml:13:13 30 | action#android.intent.action.MAIN 31 | ADDED from AndroidManifest.xml:14:17 32 | android:name 33 | ADDED from AndroidManifest.xml:14:25 34 | category#android.intent.category.LAUNCHER 35 | ADDED from AndroidManifest.xml:15:17 36 | android:name 37 | ADDED from AndroidManifest.xml:15:27 38 | activity#com.savva.TicaTacToe.Game 39 | ADDED from AndroidManifest.xml:18:9 40 | android:label 41 | ADDED from AndroidManifest.xml:20:13 42 | android:name 43 | ADDED from AndroidManifest.xml:19:13 44 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/TicaTacToe/Game.java: -------------------------------------------------------------------------------- 1 | package com.savva.TicaTacToe; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.content.SharedPreferences; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | import android.view.KeyEvent; 10 | import android.widget.Toast; 11 | 12 | import com.savva.strategies.Board; 13 | import com.savva.strategies.BoardInterface; 14 | 15 | public class Game extends Activity { 16 | 17 | public static final String PLAYER = "com.savva.TicTacToe.Game.PLAYER"; 18 | public static final String PREF_GAME = "catdogtoe"; 19 | public static final String PREF_NAME = "myPrefs"; 20 | public static final int CROSS = 1; // cross 21 | public static final int CIRCLE = 2; // circle 22 | protected static final int CONTINUE = -1; 23 | public static BoardInterface ticTacToeStrategy; 24 | public static boolean isMessagePrint; 25 | static int i = 0; 26 | static int j = 0; 27 | private int[] grid = new int[3 * 3]; 28 | private int player; 29 | private int opponent; 30 | private int curMove; 31 | private GameView gameView; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | 37 | setGrid(); 38 | // who will be first 39 | int temp = getIntent().getIntExtra(PLAYER, CIRCLE); 40 | 41 | // if continue game 42 | if (temp == -1) { 43 | getSavedState(); 44 | if (curMove == opponent) { 45 | computerMove(); 46 | } 47 | // removing old preferences 48 | SharedPreferences preferences = getSharedPreferences(PREF_GAME, 0); 49 | SharedPreferences.Editor editor = preferences.edit(); 50 | editor.clear(); 51 | editor.commit(); 52 | 53 | } 54 | // normal mode 55 | else { 56 | if (temp == 0) { 57 | opponent = CROSS; 58 | player = CIRCLE; 59 | } else { 60 | player = CROSS; 61 | opponent = CIRCLE; 62 | } 63 | // if computer is first 64 | if (opponent == 1) { 65 | computerMove(); 66 | } 67 | } 68 | gameView = new GameView(this); 69 | setContentView(gameView); 70 | gameView.requestFocus(); 71 | 72 | // If the activity is restarted, do a continue next time 73 | getIntent().putExtra(PLAYER, CONTINUE); 74 | } 75 | 76 | @Override 77 | protected void onPause() { 78 | super.onPause(); 79 | // Save the current GRID if not full 80 | if (checkGameFinished() == 0) { 81 | SharedPreferences settings = getSharedPreferences(PREF_GAME, 0); 82 | SharedPreferences.Editor editor = settings.edit(); 83 | editor.putString(PREF_NAME, setSavedState()); 84 | editor.commit(); 85 | } 86 | } 87 | 88 | /** 89 | * set data from previous unfinished game 90 | */ 91 | private void getSavedState() { 92 | String puz; 93 | String def = "000" + "000" + "000" + "121"; 94 | SharedPreferences settings = getSharedPreferences(PREF_GAME, 0); 95 | puz = settings.getString(PREF_NAME, def); 96 | 97 | for (int i = 0; i < puz.length(); i++) { 98 | if (i == puz.length() - 1) 99 | curMove = puz.charAt(i) - '0'; 100 | if (i == puz.length() - 2) 101 | opponent = puz.charAt(i) - '0'; 102 | if (i == puz.length() - 3) 103 | player = puz.charAt(i) - '0'; 104 | if (i < puz.length() - 3) 105 | grid[i] = puz.charAt(i) - '0'; 106 | } 107 | } 108 | 109 | /** 110 | * Convert an array into a grid string 111 | */ 112 | private String setSavedState() { 113 | StringBuilder buf = new StringBuilder(); 114 | for (int element : grid) { 115 | buf.append(element); 116 | } 117 | buf.append(player); 118 | Log.v("before save", "t" + player); 119 | buf.append(opponent); 120 | Log.v("before save", "t" + opponent); 121 | buf.append(curMove); 122 | Log.v("before save", "t" + curMove); 123 | return buf.toString(); 124 | } 125 | 126 | /** 127 | * set grid in 0 values, beginning state 128 | */ 129 | public void setGrid() { 130 | for (int i = 0; i < grid.length; i++) { 131 | grid[i] = 0; 132 | } 133 | // #### 134 | ticTacToeStrategy.clear(); 135 | isMessagePrint = false; 136 | if (opponent == 1) { 137 | computerMove(); 138 | } 139 | } 140 | 141 | /** 142 | * get value from grid on specified position 143 | * 144 | * @param x - column 145 | * @param y - string 146 | * @return 0 if values is wrong 147 | */ 148 | public int getGridPosition(int x, int y) { 149 | if (y * 3 + x < grid.length) 150 | return grid[y * 3 + x]; 151 | else 152 | return 0; 153 | } 154 | 155 | /** 156 | * set value from grid to specified value 157 | * 158 | * @param x - column 159 | * @param y - string 160 | * @return true if is succeeded, and false otherwise 161 | */ 162 | public boolean setGridPosition(int x, int y, int value) { 163 | if (y * 3 + x < grid.length) 164 | if (grid[y * 3 + x] == 0) { 165 | grid[y * 3 + x] = value; 166 | return true; 167 | } 168 | return false; 169 | } 170 | 171 | public int getPlayer() { 172 | return player; 173 | } 174 | 175 | public void setCurMove(int mov) { 176 | curMove = mov; 177 | } 178 | 179 | public int getOpponent() { 180 | return opponent; 181 | } 182 | 183 | /** 184 | * @return 0 - game continuing 1 - won cross, 2 - won circle, 3 - drawn game 185 | */ 186 | public int checkGameFinished() { 187 | ticTacToeStrategy.setBoardFromGrid(grid); 188 | int status = ticTacToeStrategy.isWinInt(); 189 | if (status == 0) { 190 | if(ticTacToeStrategy.isBoardFull()) { 191 | status = 3; 192 | printMessage(status); 193 | } 194 | } else 195 | printMessage(status); 196 | return status; 197 | } 198 | 199 | private void printMessage(int status) { 200 | if (isMessagePrint) return; 201 | String message = "Draw!"; 202 | if (status == 1) 203 | if (getPlayer() == 1) 204 | message = "You Win!"; 205 | else message = "You Lost!"; 206 | if (status == 2) 207 | if (getPlayer() == 2) 208 | message = "You Win!"; 209 | else message = "You Lost!"; 210 | Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); 211 | isMessagePrint = true; 212 | } 213 | /** 214 | * AI move 215 | */ 216 | public void computerMove() { 217 | String O = Board.O; 218 | String X = Board.X; 219 | if (opponent == 1) { 220 | O = Board.X; 221 | X = Board.O; 222 | } 223 | int selected = -1; 224 | ticTacToeStrategy.setBoardFromGrid(grid); 225 | Log.v("move: ", String.valueOf(selected)); 226 | grid[ticTacToeStrategy.getStrategyMove(O, X)] = opponent; 227 | } 228 | 229 | @Override 230 | public boolean onKeyDown(int keycode, KeyEvent event) { 231 | if (keycode == KeyEvent.KEYCODE_MENU) { 232 | new AlertDialog.Builder(this).setItems(R.array.optionsDialog, 233 | new DialogInterface.OnClickListener() { 234 | public void onClick(DialogInterface dialoginterface, 235 | int i) { 236 | if (i == 1) { 237 | finish(); 238 | } 239 | 240 | } 241 | }).create().show(); 242 | } 243 | return super.onKeyDown(keycode, event); 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/TicaTacToe/GameView.java: -------------------------------------------------------------------------------- 1 | package com.savva.TicaTacToe; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.util.Log; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | 13 | 14 | public class GameView extends View { 15 | 16 | private final Game game; 17 | private Bitmap backgroundGrid; 18 | private Bitmap bitmapCross; 19 | private Bitmap bitmapCircle; 20 | private int aiWins; 21 | private int userWins; 22 | private int draws; 23 | private String youLabel; 24 | private String aiLabel; 25 | 26 | public GameView(Context context) { 27 | super(context); 28 | this.game = (Game) context; 29 | 30 | aiWins = 0; 31 | userWins = 0; 32 | draws = 0; 33 | youLabel = game.getString(R.string.youLabel); 34 | aiLabel = game.getString(R.string.aiLabel); 35 | 36 | bitmapCross = BitmapFactory.decodeResource(game.getResources(), 37 | R.drawable.cross); 38 | bitmapCircle = BitmapFactory.decodeResource(game.getResources(), 39 | R.drawable.circle); 40 | backgroundGrid = BitmapFactory.decodeResource(game.getResources(), 41 | R.drawable.grid); 42 | 43 | setEnabled(true); 44 | setClickable(true); 45 | } 46 | 47 | protected void onDraw(Canvas canvas) { 48 | Paint background = new Paint(); 49 | Paint text = new Paint(); 50 | background.setColor(Color.WHITE); 51 | canvas.drawRect(0, 0, getWidth(), getHeight(), background); 52 | canvas.drawBitmap(backgroundGrid, 10, 0, null); 53 | 54 | for (int i = 0; i < 3; i++) 55 | for (int j = 0; j < 3; j++) { 56 | int pos = game.getGridPosition(i, j); 57 | if (pos == 1) { 58 | canvas.drawBitmap(bitmapCross, i 59 | * (bitmapCross.getWidth() + 20), j 60 | * (bitmapCross.getHeight() + 15), null); 61 | } 62 | if (pos == 2) { 63 | canvas.drawBitmap(bitmapCircle, i 64 | * (bitmapCircle.getWidth() + 20), j 65 | * (bitmapCircle.getHeight() + 15), null); 66 | } 67 | } 68 | //current rank 69 | text.setAntiAlias(true); 70 | text.setColor(Color.BLACK); 71 | text.setTextSize(30); 72 | canvas.drawText(youLabel, 30, backgroundGrid.getHeight() + 50, text); 73 | canvas.drawText(String.valueOf(userWins), 210, backgroundGrid.getHeight() + 50, text); 74 | 75 | canvas.drawText(aiLabel, 30, backgroundGrid.getHeight() + 90, text); 76 | canvas.drawText(String.valueOf(aiWins), 210, backgroundGrid.getHeight() + 90, text); 77 | 78 | canvas.drawText("Draw", 30, backgroundGrid.getHeight() + 130, text); 79 | canvas.drawText(String.valueOf(draws), 210, backgroundGrid.getHeight() + 130, text); 80 | } 81 | 82 | @Override 83 | public boolean onTouchEvent(MotionEvent event) { 84 | int action = event.getAction(); 85 | int winner = -1; 86 | boolean isValidMove = false; 87 | 88 | if (action == MotionEvent.ACTION_DOWN) { 89 | return true; 90 | 91 | } else if (action == MotionEvent.ACTION_UP) { 92 | int x = (int) event.getX(); 93 | int y = (int) event.getY(); 94 | 95 | // if game over reset grid, and begin new game 96 | if (game.checkGameFinished() != 0) { 97 | game.setGrid(); 98 | invalidate(); 99 | return false; 100 | } 101 | 102 | int bgWidth = (backgroundGrid.getWidth() + 10) / 3; 103 | int bgHeight = backgroundGrid.getHeight() / 3; 104 | 105 | x = x / bgWidth; 106 | y = y / bgHeight; 107 | Log.v("test x:y", String.valueOf(x) + " : " + String.valueOf(y)); 108 | 109 | if (x < 3 && x >= 0 && y < 3 && y >= 0) { 110 | isValidMove = game.setGridPosition(x, y, game.getPlayer()); 111 | } 112 | if (isValidMove == true) { 113 | winner = game.checkGameFinished(); 114 | 115 | if (winner == 0) { 116 | game.setCurMove(game.getOpponent()); 117 | game.computerMove(); 118 | } 119 | winner = game.checkGameFinished(); 120 | 121 | if (winner == 1) { 122 | if (game.getPlayer() == 1) { 123 | userWins++; 124 | } else { 125 | aiWins++; 126 | } 127 | } 128 | if (winner == 2) { 129 | if (game.getPlayer() == 2) { 130 | userWins++; 131 | } else { 132 | aiWins++; 133 | } 134 | } else if (winner == 3) { 135 | draws++; 136 | } 137 | } 138 | } 139 | game.setCurMove(game.getPlayer()); 140 | invalidate(); 141 | return false; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/TicaTacToe/TicTacToe.java: -------------------------------------------------------------------------------- 1 | package com.savva.TicaTacToe; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.view.View.OnClickListener; 10 | 11 | import com.savva.strategies.Board; 12 | import com.savva.strategies.BoardDumb; 13 | import com.savva.strategies.BoardHard; 14 | 15 | public class TicTacToe extends Activity implements OnClickListener { 16 | 17 | private View easyButton; 18 | private View exitButton; 19 | private View mediumButton; 20 | private View hardButton; 21 | 22 | /** 23 | * Called when the activity is first created. 24 | */ 25 | @Override 26 | public void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.main); 29 | 30 | easyButton = findViewById(R.id.EasyDifficulty); 31 | easyButton.setOnClickListener(this); 32 | mediumButton = findViewById(R.id.MediumDifficulty); 33 | mediumButton.setOnClickListener(this); 34 | hardButton = findViewById(R.id.HardDifficulty); 35 | hardButton.setOnClickListener(this); 36 | 37 | exitButton = findViewById(R.id.exitButton); 38 | exitButton.setOnClickListener(this); 39 | } 40 | 41 | @Override 42 | public void onClick(View v) { 43 | switch (v.getId()) { 44 | 45 | case R.id.MediumDifficulty: 46 | Game.ticTacToeStrategy = new Board(); 47 | newGameDialog(); 48 | break; 49 | case R.id.HardDifficulty: 50 | Game.ticTacToeStrategy = new BoardHard(); 51 | newGameDialog(); 52 | break; 53 | case R.id.EasyDifficulty: 54 | Game.ticTacToeStrategy = new BoardDumb(); 55 | newGameDialog(); 56 | break; 57 | case R.id.exitButton: 58 | finish(); 59 | break; 60 | } 61 | } 62 | 63 | private void newGameDialog() { 64 | new AlertDialog.Builder(this) 65 | .setTitle(R.string.whosFirstLabel) 66 | .setItems(R.array.whosFirst, 67 | new DialogInterface.OnClickListener() { 68 | public void onClick( 69 | DialogInterface dialoginterface, int i) { 70 | startGame(i); 71 | } 72 | }).show(); 73 | } 74 | 75 | /** 76 | * @param i - 0 AI moves first, 1 User moves first 77 | */ 78 | private void startGame(int i) { 79 | Intent intent = new Intent(TicTacToe.this, Game.class); 80 | intent.putExtra(Game.PLAYER, i); 81 | startActivity(intent); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/strategies/Board.java: -------------------------------------------------------------------------------- 1 | package com.savva.strategies; 2 | 3 | import java.util.Random; 4 | 5 | 6 | /** 7 | * @author Savva Vyatkin 8 | * the class for the medium difficulty 9 | * is extended by impossible and easy 10 | */ 11 | 12 | public class Board implements BoardInterface { 13 | 14 | /** 15 | * Game Tic Tac Toe 16 | */ 17 | public static String O = "O"; 18 | public static String X = "X"; 19 | /** 20 | * board - contains board 3x3 21 | */ 22 | protected String[][] board = new String[3][3]; 23 | 24 | public Board() { 25 | this.clear(); 26 | } 27 | 28 | public Board(String[][] board) { 29 | this.board = board; 30 | } 31 | 32 | public void clear() { 33 | { 34 | for (int i = 0; i < 3; i++) 35 | for (int j = 0; j < 3; j++) 36 | this.board[i][j] = " "; 37 | } 38 | } 39 | 40 | /** 41 | * @param i - check column #i 42 | * @return - true if full up with the same player code 43 | */ 44 | 45 | private boolean checkColumn(int i) { 46 | String s = this.board[0][i] + this.board[1][i] + this.board[2][i]; 47 | return (s).equalsIgnoreCase("OOO") || (s).equalsIgnoreCase("XXX"); 48 | } 49 | 50 | /** 51 | * @param i - row 52 | * @param j - column 53 | * @param x2 - X or O 54 | * @param b - current board 55 | * @return true if its i and j - win code 56 | */ 57 | 58 | private boolean isWinMove(int i, int j, String x2, BoardInterface b) { 59 | if (b.getBoardElement(i, j).equals(" ")) { 60 | b.setBoardElement(x2, i, j); 61 | if (b.isWin()) 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | /** 68 | * override clone method 69 | */ 70 | 71 | @Override 72 | protected BoardInterface clone() { 73 | String[][] defendsCopy = new String[3][3]; 74 | for (int i = 0; i < 3; i++) 75 | System.arraycopy(this.board[i], 0, defendsCopy[i], 0, 3); 76 | return new Board(defendsCopy); 77 | 78 | } 79 | 80 | /* 81 | * program checks if it can win with winMove method 82 | */ 83 | 84 | @Override 85 | public int winMove(String o2) { 86 | for (int i = 0; i < 3; i++) { 87 | for (int j = 0; j < 3; j++) { 88 | if (this.board[i][j].equals(" ")) { 89 | if (isWinMove(i, j, o2, this.clone())) { 90 | this.board[i][j] = o2; 91 | return i * 3 + j; 92 | } 93 | } 94 | } 95 | } 96 | return -1; 97 | } 98 | 99 | /* 100 | * Program checks if the player can win with winEnemyMove method 101 | */ 102 | @Override 103 | public int winEnemyMove(String x2, String o2) { 104 | for (int i = 0; i < 3; i++) 105 | for (int j = 0; j < 3; j++) 106 | if (this.board[i][j].equals(" ")) 107 | if (isWinMove(i, j, x2, this.clone())) { 108 | this.board[i][j] = o2; 109 | return i * 3 + j; 110 | } 111 | return -1; 112 | } 113 | 114 | /** 115 | * checks if the spot is taken 116 | */ 117 | // protected boolean isBoardEmpty() { 118 | // for (int i = 0; i < 3; i++) 119 | // for (int j = 0; j < 3; j++) 120 | // if (!this.board[i][j].equals(" ")) 121 | // return false; 122 | // return true; 123 | // } 124 | 125 | /* 126 | * checks if the board is full 127 | */ 128 | @Override 129 | public boolean isBoardFull() { 130 | for (int i = 0; i < 3; i++) 131 | for (int j = 0; j < 3; j++) 132 | if (this.board[i][j].equals(" ")) 133 | return false; 134 | return true; 135 | } 136 | 137 | /* 138 | * checks if someone won 139 | */ 140 | @Override 141 | public boolean isWin() { 142 | for (int i = 0; i < 3; i++) { 143 | if (checkColumn(i)) 144 | return true; 145 | if (checkRow(i)) 146 | return true; 147 | } 148 | return checkDiagonal1() || checkDiagonal2(); 149 | } 150 | 151 | @Override 152 | public int isWinInt() { 153 | for (int i = 0; i < 3; i++) { 154 | if (checkColumn(i)) 155 | return this.board[0][i].equals(Board.X)? 1:2; 156 | if (checkRow(i)) 157 | return this.board[i][0].equals(Board.X)? 1:2; 158 | } 159 | if (checkDiagonal1() || checkDiagonal2()) 160 | return this.board[1][1].equals(Board.X)? 1:2; 161 | return 0; 162 | } 163 | /** 164 | * @param i represents the row 165 | * @return true if there is a win on the rows 166 | */ 167 | private boolean checkRow(int i) { 168 | String s = this.board[i][0] + this.board[i][1] + this.board[i][2]; 169 | return (s).equalsIgnoreCase("OOO") || (s).equalsIgnoreCase("XXX"); 170 | } 171 | 172 | /** 173 | * @return true if there is a win on the first diagonal 174 | */ 175 | private boolean checkDiagonal1() { 176 | String s = this.board[0][0] + this.board[1][1] + this.board[2][2]; 177 | return (s).equalsIgnoreCase("OOO") || (s).equalsIgnoreCase("XXX"); 178 | } 179 | 180 | /** 181 | * @return true if there is a win on the second diagonal 182 | */ 183 | private boolean checkDiagonal2() { 184 | String s = this.board[0][2] + this.board[1][1] + this.board[2][0]; 185 | return (s).equalsIgnoreCase("OOO") || (s).equalsIgnoreCase("XXX"); 186 | } 187 | 188 | @Override 189 | public String[][] getBoard() { 190 | return board; 191 | } 192 | 193 | @Override 194 | public void setBoard(String[][] board) { 195 | this.board = board; 196 | } 197 | 198 | @Override 199 | public String getBoardElement(int i, int j) { 200 | return board[i][j]; 201 | } 202 | 203 | @Override 204 | public void setBoardElement(String x2, int i, int j) { 205 | this.board[i][j] = x2; 206 | } 207 | 208 | /* 209 | * the program either takes the center or makes a random move 210 | */ 211 | @Override 212 | public int move(String o2) { 213 | if (this.board[1][1].equals(" ")) { 214 | this.board[1][1] = o2; 215 | return 4; 216 | } 217 | return getRandomMove(o2); 218 | } 219 | 220 | protected boolean checkCorner(int i, int j) { 221 | return this.board[i][j].equals(" "); 222 | } 223 | 224 | protected boolean checkElementTrue(int i, int j, String value) { 225 | return this.board[i][j].equals(value); 226 | } 227 | 228 | 229 | @Override 230 | public String toString() { 231 | String s; 232 | 233 | s = board[0][0] + "|" + board[0][1] + "|" + board[0][2] + "\n"; 234 | s += "------" + "\n"; 235 | s += board[1][0] + "|" + board[1][1] + "|" + board[1][2] + "\n"; 236 | s += "------" + "\n"; 237 | s += board[2][0] + "|" + board[2][1] + "|" + board[2][2] + "\n"; 238 | return s; 239 | } 240 | 241 | @Override 242 | public void setBoardFromGrid(int[] grid) { 243 | for (int i = 0; i < 9; i++) 244 | if (grid[i] == 0) this.setBoardElement(" ", i / 3, i % 3); 245 | else if (grid[i] == 1) this.setBoardElement(Board.X, i / 3, i % 3); 246 | else if (grid[i] == 2) this.setBoardElement(Board.O, i / 3, i % 3); 247 | 248 | } 249 | 250 | @Override 251 | public int getStrategyMove(String O, String X) { 252 | int selected; 253 | int m = winMove(O); 254 | if (m != -1) { 255 | selected = m; 256 | } else { 257 | int mEnemyWin = winEnemyMove(X, O); 258 | if (mEnemyWin == -1) { 259 | selected = move(O); 260 | } else 261 | selected = mEnemyWin; 262 | } 263 | return selected; 264 | } 265 | 266 | public int getRandomMove(String o2) { 267 | int i, j; 268 | Random rn = new Random(); 269 | int r; 270 | do { 271 | r = rn.nextInt(9); 272 | i = r / 3; 273 | j = r % 3; 274 | } while (!board[i][j].equals(" ")); 275 | this.board[i][j] = o2; 276 | return r; 277 | } 278 | 279 | public int getSize() { 280 | int size = 0; 281 | for (int i = 0; i < 3; i++) 282 | for (int j = 0; j < 3; j++) 283 | if (!this.board[i][j].equals(" ")) 284 | size++; 285 | return size; 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/strategies/BoardDumb.java: -------------------------------------------------------------------------------- 1 | package com.savva.strategies; 2 | 3 | import java.util.Random; 4 | 5 | public class BoardDumb extends Board { 6 | 7 | // completely random moves 8 | 9 | @Override 10 | public int move(String o2) { 11 | if (this.isBoardFull()) return -1; 12 | int i = 0, j = 0; 13 | int r; 14 | Random rn = new Random(); 15 | do { 16 | r = rn.nextInt(9); 17 | i = r / 3; 18 | j = r % 3; 19 | } 20 | while (board[i][j] != " "); 21 | this.board[i][j] = o2; 22 | 23 | return r; 24 | } 25 | 26 | @Override 27 | public int getStrategyMove(String o2, String x) { 28 | if (this.isBoardFull()) 29 | return -1; 30 | int r = winMove(o2); 31 | if (r == -1) { 32 | r = getRandomMove(o2); 33 | } 34 | return r; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/strategies/BoardHard.java: -------------------------------------------------------------------------------- 1 | package com.savva.strategies; 2 | 3 | public class BoardHard extends Board { 4 | 5 | //checks to see if certain strategies are attempted by the player 6 | 7 | @Override 8 | public int move(String value) { 9 | if (this.board[1][1].equals(" ")) { 10 | this.board[1][1] = value; 11 | return 4; 12 | } 13 | String x; 14 | if (value.equals(O)) x = X; 15 | else x = O; 16 | // Encirclement tactic 17 | if (getSize() == 3) { 18 | if ((checkElementTrue(1, 1, value) && ((checkElementTrue(2, 2, x) && checkElementTrue(0, 0, x)) || (checkElementTrue(0, 2, x) && checkElementTrue(2, 0, x))))) 19 | return 1; 20 | } 21 | // Arrowhead tactic 22 | // check corners 00 02 20 22 with neighbor 23 | if (checkCorner(0, 0) && (checkElementTrue(0, 1, x) || checkElementTrue(1, 0, x))) { 24 | this.board[0][0] = value; 25 | return 0; 26 | } 27 | if (checkCorner(0, 2) && (checkElementTrue(0, 1, x) || checkElementTrue(1, 2, x))) { 28 | this.board[0][2] = value; 29 | return 2; 30 | } 31 | if (checkCorner(2, 0) && (checkElementTrue(1, 0, x) || checkElementTrue(2, 1, x))) { 32 | this.board[2][0] = value; 33 | return 6; 34 | } 35 | if (checkCorner(2, 2) && (checkElementTrue(1, 2, x) || checkElementTrue(2, 1, x))) { 36 | this.board[2][2] = value; 37 | return 8; 38 | } 39 | 40 | // Triangle tactic 41 | // check corners 00 02 20 22 42 | if (checkCorner(0, 0)) { 43 | this.board[0][0] = value; 44 | return 0; 45 | } 46 | if (checkCorner(0, 2)) { 47 | this.board[0][2] = value; 48 | return 2; 49 | } 50 | if (checkCorner(2, 0)) { 51 | this.board[2][0] = value; 52 | return 6; 53 | } 54 | if (checkCorner(2, 2)) { 55 | this.board[2][2] = value; 56 | return 8; 57 | } 58 | 59 | return getRandomMove(value); 60 | } 61 | 62 | @Override 63 | public int getStrategyMove(String oValue, String xValue) { 64 | int selected; 65 | if (getBoardElement(1, 1).equals(" ")) { 66 | selected = 4; 67 | 68 | } else { 69 | int m = winMove(oValue); 70 | if (m != -1) { 71 | selected = m; 72 | } else { 73 | int mEnemyWin = winEnemyMove(xValue, oValue); 74 | if (mEnemyWin == -1) { 75 | selected = move(oValue); 76 | } else 77 | selected = mEnemyWin; 78 | } 79 | } 80 | return selected; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/savva/strategies/BoardInterface.java: -------------------------------------------------------------------------------- 1 | package com.savva.strategies; 2 | 3 | public interface BoardInterface { 4 | 5 | /** 6 | * @param o2 -X or O 7 | * @return- true if winning move is found 8 | */ 9 | 10 | public abstract int winMove(String o2); 11 | 12 | /** 13 | * @param x2 what the AI uses 14 | * @param o2 what the player uses 15 | * @return 16 | */ 17 | public abstract int winEnemyMove(String x2, String o2); 18 | 19 | /** 20 | * @return false if board is not full 21 | */ 22 | public abstract boolean isBoardFull(); 23 | 24 | /** 25 | * @return true if winning move is found 26 | */ 27 | public boolean isWin(); 28 | 29 | public int isWinInt(); 30 | 31 | /** 32 | * @return gives results to the original board 33 | */ 34 | public abstract String[][] getBoard(); 35 | 36 | public abstract void setBoard(String[][] board); 37 | 38 | public abstract String getBoardElement(int i, int j); 39 | 40 | public abstract void setBoardElement(String x2, int i, int j); 41 | 42 | public abstract int move(String o2); 43 | 44 | public abstract String toString(); 45 | 46 | public abstract void clear(); 47 | 48 | public abstract void setBoardFromGrid(int[] grid); 49 | 50 | int getStrategyMove(String O, String X); 51 | 52 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SavvaVyatkin/CatDogToe/725a849ac3f3d18c512a823394bde451c20d1cee/app/src/main/res/drawable/circle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SavvaVyatkin/CatDogToe/725a849ac3f3d18c512a823394bde451c20d1cee/app/src/main/res/drawable/cross.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SavvaVyatkin/CatDogToe/725a849ac3f3d18c512a823394bde451c20d1cee/app/src/main/res/drawable/grid.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SavvaVyatkin/CatDogToe/725a849ac3f3d18c512a823394bde451c20d1cee/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /app/src/main/res/layout/gamefield.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 |