-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAIPlayer.java
More file actions
137 lines (113 loc) · 4.73 KB
/
AIPlayer.java
File metadata and controls
137 lines (113 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import java.util.*;
public class AIPlayer extends Player {
public AIPlayer(String name) {
super(name);
}
public boolean makeMove(GameModel model) {
Board board = model.getBoard();
boolean firstMove = model.isFirstMove();
String bestWord = null;
int bestRow = -1, bestCol = -1;
boolean bestHorizontal = true;
int maxScore = -1;
// Build the rack with '*' representing blanks
List<Character> rackLetters = new ArrayList<>();
for (Tile t : getRack()) {
rackLetters.add(t.isBlank() ? '*' : t.getLetter());
}
// Test every dictionary word
for (String w : model.getDictionary().getAllWords()) {
String word = w.toUpperCase();
// Must be buildable from rack (+ blanks)
if (!canFormWord(word, rackLetters)) continue;
for (int r = 0; r < 15; r++) {
for (int c = 0; c < 15; c++) {
for (boolean horizontal : new boolean[]{true, false}) {
// First move forced to center
if (firstMove) {
r = 7;
c = 7;
} else {
// Must attach to a tile on the board
if (!connectsToBoard(board, word, r, c, horizontal)) continue;
}
// Must be placeable using rack (blank substitution handled inside your board logic)
if (!board.canPlaceWordWithRack(word, r, c, horizontal, this)) continue;
// NEW letters cannot be adjacent perpendicularly to existing letters
if (!checkNeighbors(board, word, r, c, horizontal)) continue;
// Score move
int score = model.computeWordScore(word, r, c, horizontal, null);
// Track best scoring move
if (score > maxScore) {
maxScore = score;
bestWord = word;
bestRow = r;
bestCol = c;
bestHorizontal = horizontal;
}
if (firstMove) break;
}
if (firstMove) break;
}
if (firstMove) break;
}
}
// If we found a move → place it
if (bestWord != null) {
System.out.println("AI placing word: " + bestWord +
" at (" + bestRow + "," + bestCol + ")" +
(bestHorizontal ? " horizontally" : " vertically"));
model.placeWord(bestWord, bestRow, bestCol, bestHorizontal);
return true;
}
// Otherwise → pass turn
model.passTurn();
return false;
}
// --------------------------
// BLANK TILE SUPPORT
// --------------------------
private boolean canFormWord(String word, List<Character> rackLetters) {
List<Character> copy = new ArrayList<>(rackLetters);
for (char c : word.toCharArray()) {
if (copy.contains(c)) {
copy.remove((Character) c);
} else if (copy.contains('*')) {
copy.remove((Character) '*'); // blank used
} else {
return false;
}
}
return true;
}
// Must touch at least one tile on board (except first move)
private boolean connectsToBoard(Board board, String word, int row, int col, boolean horizontal) {
for (int i = 0; i < word.length(); i++) {
int r = row + (horizontal ? 0 : i);
int c = col + (horizontal ? i : 0);
if (board.squareHasTile(r, c)) return true;
}
return false;
}
// New letters cannot have perpendicular neighbors
private boolean checkNeighbors(Board board, String word, int row, int col, boolean horizontal) {
for (int i = 0; i < word.length(); i++) {
int r = row + (horizontal ? 0 : i);
int c = col + (horizontal ? i : 0);
// Do NOT check neighbors for letters already on the board
if (board.squareHasTile(r, c)) continue;
if (horizontal) {
if ((r > 0 && board.squareHasTile(r - 1, c)) ||
(r < 14 && board.squareHasTile(r + 1, c))) {
return false;
}
} else {
if ((c > 0 && board.squareHasTile(r, c - 1)) ||
(c < 14 && board.squareHasTile(r, c + 1))) {
return false;
}
}
}
return true;
}
}