Skip to content

Commit d0cc5e8

Browse files
committed
v0.2 - add X or O options in 1 Player mode, minor code cleanup and optimization
1 parent 66cd015 commit d0cc5e8

7 files changed

Lines changed: 98 additions & 50 deletions

File tree

.idea/deploymentTargetSelector.xml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
# OpenTicTacToe (OpenTTT) v0.1
1+
# OpenTicTacToe (OpenTTT)
22
### Play your (probably) favorite FOSS game on your (probably) favorite android.
33
<img width="303" height="640" alt="image" src="https://github.com/user-attachments/assets/0de0fd35-a0bc-46b8-823f-6ab05507d3a8" />
44

55
For now it features:
66
- Responsive views (aka buttons don't go crazy depending on your screen size)
7-
- 1 Player mode (an AI that just presses buttons randomly (better than nothing though))
8-
- 2 Player mode
7+
- 1 Player mode (an AI that just presses buttons randomly (better than nothing though); (You can also pick between going X or O!))
8+
- 2 Player mode
99
- Nice and simple UI
1010
- Supports Android 7 and higher
1111
- Personally tested on numerous devices (Xiaomi Note 13 Pro, Lenovo K3 Note, Lenovo Tab 4 8 (TB-8504F))
1212

1313
TODO:
1414
- Ukrainian localization
15-
- Have player choose between going X or O in 1 Player mode
1615
- Light mode
1716
- Custom app icon
1817
- Much nicer design, at least the main menu

app/src/main/java/com/example/opentictactoe/gameplay_2.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
import android.view.View;
55
import android.widget.Button;
66
import android.widget.TextView;
7-
87
import androidx.appcompat.app.AppCompatActivity;
98

10-
119
public class gameplay_2 extends AppCompatActivity {
1210
@Override
1311
protected void onCreate(Bundle savedInstanceState) {
@@ -16,12 +14,12 @@ protected void onCreate(Bundle savedInstanceState) {
1614
}
1715
int turn = 0;
1816
String[][] board = new String[3][3];
19-
private void disableAllButtons() {
20-
int[] ids = {
21-
R.id.button00, R.id.button01, R.id.button02,
22-
R.id.button10, R.id.button11, R.id.button12,
23-
R.id.button20, R.id.button21, R.id.button22
24-
};
17+
int[] ids = {
18+
R.id.button00, R.id.button01, R.id.button02,
19+
R.id.button10, R.id.button11, R.id.button12,
20+
R.id.button20, R.id.button21, R.id.button22
21+
};
22+
private void disableAllButtons(int[] ids) {
2523
for (int id : ids) {
2624
findViewById(id).setEnabled(false);
2725
}
@@ -58,7 +56,6 @@ public boolean checkDraw(String[][] board) {
5856
if (board[r][c] == null) return false;
5957
}
6058
}
61-
// If no empty cells AND no win → it's a draw
6259
return !checkWin(board);
6360
}
6461
public void onPress(View v){
@@ -68,7 +65,6 @@ public void onPress(View v){
6865
int row = Character.getNumericValue(tag.charAt(0));
6966
int col = Character.getNumericValue(tag.charAt(2));
7067

71-
// Set X and O accordingly
7268
if (turn == 0) {
7369
board[row][col] = "X";
7470
b.setText("X");
@@ -78,13 +74,13 @@ public void onPress(View v){
7874
b.setText("O");
7975
tv.setText("It's X's turn!");
8076
}
81-
// Then make the button disabled
77+
8278
v.setEnabled(false);
83-
// Then check for a win
79+
8480
if (checkWin(board)) {
8581
tv.setText(turn == 0 ? "X wins!" : "O wins!");
8682
// Disable all buttons since we won
87-
disableAllButtons();
83+
disableAllButtons(ids);
8884
return;
8985
}
9086
if (checkDraw(board)) {

app/src/main/java/com/example/opentictactoe/gameplay_ai.java

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,32 @@
99
import androidx.appcompat.app.AppCompatActivity;
1010
import java.util.Random;
1111

12-
1312
public class gameplay_ai extends AppCompatActivity {
1413
@Override
1514
protected void onCreate(Bundle savedInstanceState) {
1615
super.onCreate(savedInstanceState);
17-
setContentView(R.layout.activity_gameplay_2);
16+
setContentView(R.layout.activity_gameplay_ai);
17+
setBoardVisibility(View.GONE, ids);
1818
}
1919
int turn = 0;
20+
String playerSymbol;
21+
String aiSymbol;
2022
String[][] board = new String[3][3];
21-
private void disableAllButtons() {
22-
int[] ids = {
23-
R.id.button00, R.id.button01, R.id.button02,
24-
R.id.button10, R.id.button11, R.id.button12,
25-
R.id.button20, R.id.button21, R.id.button22
26-
};
23+
int[] ids = {
24+
R.id.button00, R.id.button01, R.id.button02,
25+
R.id.button10, R.id.button11, R.id.button12,
26+
R.id.button20, R.id.button21, R.id.button22
27+
};
28+
private void disableAllButtons(int[] ids) {
2729
for (int id : ids) {
2830
findViewById(id).setEnabled(false);
2931
}
3032
}
33+
private void setBoardVisibility(int visibility, int[] ids) {
34+
for (int id : ids) {
35+
findViewById(id).setVisibility(visibility);
36+
}
37+
}
3138
public boolean checkWin(String[][] board) {
3239
// rows
3340
for (int r = 0; r < 3; r++) {
@@ -52,15 +59,30 @@ public boolean checkWin(String[][] board) {
5259
return false;
5360
}
5461
public boolean checkDraw(String[][] board) {
55-
// If ANY cell is still null, it's not a draw
5662
for (int r = 0; r < 3; r++) {
5763
for (int c = 0; c < 3; c++) {
5864
if (board[r][c] == null) return false;
5965
}
6066
}
61-
// If no empty cells AND no win → it's a draw
6267
return !checkWin(board);
6368
}
69+
public void startAsX(View v){
70+
playerSymbol = "X";
71+
aiSymbol = "O";
72+
findViewById(R.id.buttonStartX).setVisibility(View.GONE);
73+
findViewById(R.id.buttonStartO).setVisibility(View.GONE);
74+
setBoardVisibility(View.VISIBLE, ids);
75+
TextView tv = findViewById(R.id.turnIndicator);
76+
tv.setText("Your turn!");
77+
}
78+
public void startAsO(View v){
79+
playerSymbol = "O";
80+
aiSymbol = "X";
81+
findViewById(R.id.buttonStartX).setVisibility(View.GONE);
82+
findViewById(R.id.buttonStartO).setVisibility(View.GONE);
83+
setBoardVisibility(View.VISIBLE, ids);
84+
makeAIMove(findViewById(R.id.turnIndicator));
85+
}
6486
private void makeAIMove(TextView tv) {
6587
Random r = new Random();
6688
int row, col;
@@ -71,53 +93,49 @@ private void makeAIMove(TextView tv) {
7193
col = r.nextInt(3);
7294
} while (board[row][col] != null);
7395

74-
// Get the button by ID
7596
String buttonID = "button" + row + col;
7697
int id = getResources().getIdentifier(buttonID, "id", getPackageName());
7798
Button b = findViewById(id);
7899

79-
// AI always plays O
80-
board[row][col] = "O";
81-
b.setText("O");
100+
board[row][col] = aiSymbol;
101+
b.setText(aiSymbol);
82102
b.setEnabled(false);
83103
tv.setText("Your turn!");
84104

85105
if (checkWin(board)) {
86-
tv.setText("O wins!");
87-
disableAllButtons();
106+
tv.setText(aiSymbol + " wins!");
107+
disableAllButtons(ids);
88108
return;
89109
}
90110
if (checkDraw(board)) {
91111
tv.setText("It's a draw!");
92-
disableAllButtons();
112+
disableAllButtons(ids);
93113
return;
94114
}
95115
turn = 0; // Back to player
96116
}
97117
public void onPress(View v){
98-
Button b = (Button) v;
99-
TextView tv = findViewById(R.id.turnIndicator);
100118
String tag = (String) v.getTag();
101119
int row = Character.getNumericValue(tag.charAt(0));
102120
int col = Character.getNumericValue(tag.charAt(2));
103-
104-
// Set X and O accordingly
105-
board[row][col] = "X";
106-
b.setText("X");
121+
Button b = (Button) v;
122+
TextView tv = findViewById(R.id.turnIndicator);
123+
board[row][col] = playerSymbol;
124+
b.setText(playerSymbol);
107125
v.setEnabled(false);
108126

109127
if (checkWin(board)) {
110-
tv.setText("X wins!");
111-
disableAllButtons();
128+
tv.setText(playerSymbol + " wins!");
129+
disableAllButtons(ids);
112130
return;
113131
}
114132
if (checkDraw(board)) {
115133
tv.setText("It's a draw!");
116-
disableAllButtons();
134+
disableAllButtons(ids);
117135
return;
118136
}
119-
tv.setText("Thinking...");
120137

138+
tv.setText("Thinking...");
121139
getWindow().setFlags(
122140
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
123141
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<shape xmlns:android="http://schemas.android.com/apk/res/android">
3-
<solid android:color="#FF6200EE"/> <!-- Button background color -->
3+
<solid android:color="#FF49352C"/> <!-- Button background color -->
44
<corners android:radius="32dp"/> <!-- Corner radius -->
55
</shape>

app/src/main/res/layout/activity_gameplay_ai.xml

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
android:background="#140C0C"
99
android:backgroundTint="#433540"
1010
android:splitMotionEvents="false"
11-
tools:context=".gameplay_2">
11+
tools:context=".gameplay_ai">
1212

1313
<Button
1414
android:id="@+id/button21"
@@ -19,6 +19,7 @@
1919
android:onClick="onPress"
2020
android:tag="2,1"
2121
android:textSize="48sp"
22+
android:visibility="gone"
2223
app:layout_constraintBottom_toBottomOf="parent"
2324
app:layout_constraintEnd_toEndOf="parent"
2425
app:layout_constraintHorizontal_bias="0.498"
@@ -36,6 +37,7 @@
3637
android:onClick="onPress"
3738
android:tag="2,2"
3839
android:textSize="48sp"
40+
android:visibility="gone"
3941
app:layout_constraintBottom_toBottomOf="parent"
4042
app:layout_constraintEnd_toEndOf="parent"
4143
app:layout_constraintHorizontal_bias="0.062"
@@ -53,6 +55,7 @@
5355
android:onClick="onPress"
5456
android:tag="2,0"
5557
android:textSize="48sp"
58+
android:visibility="gone"
5659
app:layout_constraintBottom_toBottomOf="parent"
5760
app:layout_constraintEnd_toStartOf="@+id/button21"
5861
app:layout_constraintHorizontal_bias="1.0"
@@ -69,6 +72,7 @@
6972
android:onClick="onPress"
7073
android:tag="1,0"
7174
android:textSize="48sp"
75+
android:visibility="gone"
7276
app:layout_constraintBottom_toBottomOf="parent"
7377
app:layout_constraintEnd_toStartOf="@+id/button11"
7478
app:layout_constraintHorizontal_bias="1.0"
@@ -84,6 +88,7 @@
8488
android:onClick="onPress"
8589
android:tag="1,1"
8690
android:textSize="48sp"
91+
android:visibility="gone"
8792
app:layout_constraintBottom_toBottomOf="parent"
8893
app:layout_constraintEnd_toEndOf="parent"
8994
app:layout_constraintHorizontal_bias="0.498"
@@ -100,6 +105,7 @@
100105
android:onClick="onPress"
101106
android:tag="1,2"
102107
android:textSize="48sp"
108+
android:visibility="gone"
103109
app:layout_constraintBottom_toBottomOf="parent"
104110
app:layout_constraintEnd_toEndOf="parent"
105111
app:layout_constraintHorizontal_bias="0.062"
@@ -117,6 +123,7 @@
117123
android:onClick="onPress"
118124
android:tag="0,0"
119125
android:textSize="48sp"
126+
android:visibility="gone"
120127
app:layout_constraintBottom_toTopOf="@+id/button10"
121128
app:layout_constraintEnd_toStartOf="@+id/button01"
122129
app:layout_constraintHorizontal_bias="1.0"
@@ -134,6 +141,7 @@
134141
android:onClick="onPress"
135142
android:tag="0,2"
136143
android:textSize="48sp"
144+
android:visibility="gone"
137145
app:layout_constraintBottom_toTopOf="@+id/button12"
138146
app:layout_constraintEnd_toEndOf="parent"
139147
app:layout_constraintHorizontal_bias="0.0"
@@ -150,6 +158,7 @@
150158
android:onClick="onPress"
151159
android:tag="0,1"
152160
android:textSize="48sp"
161+
android:visibility="gone"
153162
app:layout_constraintBottom_toTopOf="@+id/button11"
154163
app:layout_constraintEnd_toEndOf="parent"
155164
app:layout_constraintHorizontal_bias="0.501"
@@ -161,7 +170,7 @@
161170
android:id="@+id/turnIndicator"
162171
android:layout_width="284dp"
163172
android:layout_height="70dp"
164-
android:text="It's X's turn!"
173+
android:text="It's..."
165174
android:textAlignment="center"
166175
android:textColor="#FFFFFF"
167176
android:textSize="48sp"
@@ -197,4 +206,30 @@
197206
app:layout_constraintStart_toStartOf="parent"
198207
app:layout_constraintTop_toTopOf="parent" />
199208

209+
<Button
210+
android:id="@+id/buttonStartX"
211+
android:layout_width="wrap_content"
212+
android:layout_height="wrap_content"
213+
android:onClick="startAsX"
214+
android:text="Start as X"
215+
app:layout_constraintBottom_toBottomOf="parent"
216+
app:layout_constraintEnd_toEndOf="parent"
217+
app:layout_constraintHorizontal_bias="0.498"
218+
app:layout_constraintStart_toStartOf="parent"
219+
app:layout_constraintTop_toTopOf="parent"
220+
app:layout_constraintVertical_bias="0.341" />
221+
222+
<Button
223+
android:id="@+id/buttonStartO"
224+
android:layout_width="wrap_content"
225+
android:layout_height="wrap_content"
226+
android:onClick="startAsO"
227+
android:text="Start as O"
228+
app:layout_constraintBottom_toBottomOf="parent"
229+
app:layout_constraintEnd_toEndOf="parent"
230+
app:layout_constraintHorizontal_bias="0.498"
231+
app:layout_constraintStart_toStartOf="parent"
232+
app:layout_constraintTop_toBottomOf="@+id/buttonStartX"
233+
app:layout_constraintVertical_bias="0.0" />
234+
200235
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/layout/activity_main.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
android:layout_width="wrap_content"
7878
android:layout_height="wrap_content"
7979
android:layout_marginBottom="16dp"
80-
android:text=" v0.1\nby unkver"
80+
android:text=" v0.2\nby unkver"
8181
android:textColor="#EDEDED"
8282
app:layout_constraintBottom_toBottomOf="parent"
8383
app:layout_constraintEnd_toEndOf="parent"

0 commit comments

Comments
 (0)