-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDiceClient.java
More file actions
234 lines (206 loc) · 9.91 KB
/
DiceClient.java
File metadata and controls
234 lines (206 loc) · 9.91 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Random;
public class DiceClient {
private Socket socket = null;
private DataInputStream dis = null;
private DataOutputStream dos = null;
private String githubId = "jlim5634";
private String avatarName = "JLim";
private Random random = new Random();
private int[] numDice;
private static final int MAX_FACE = 6;
private static final double BLUFF_CHANCE = 0.15;
private static final int DEFAULT_BID = 1;
private static final int DEFAULT_VALUE = 2;
public DiceClient(String IpAddress, int IpPort) throws IOException {
socket = new Socket(IpAddress, Integer.valueOf(IpPort));
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
}
private void write(String s) throws IOException {
dos.writeUTF(s);
dos.flush();
}
private String read() throws IOException {
return dis.readUTF();
}
private String getNextSmartBid(int totalDice, int currentCount, int currentValue, int[] count, int knownDiceCount, int lastBidCount, int lastBidValue) {
int wildCardCount = count[1]; //counts amount of 1's I have since it's a wildcard
for (int countToTry = lastBidCount; countToTry <= totalDice; countToTry++) { //loops through possible quantities to bid and starts at the current amount. the point is to find the lowest bid which is most likely to be true
for (int face = 2; face <= MAX_FACE; face++) { //loops through faces 2-6 and skips 1 because 1 is wildcard.
if (countToTry == lastBidCount && face <= lastBidValue) {
continue; //avoids repeating the previous bid so it's valid
}
int knownCount = count[face] + wildCardCount;
int unknownDice = totalDice - knownDiceCount;
double expected = knownCount + (unknownDice / 6.0);
if (countToTry <= totalDice && expected >= countToTry - 0.5) { //if expected number of dice is large enough and if it's not the same as previous bid, it plays it
int safeCount = Math.min(countToTry, totalDice);
return "play:" + safeCount + ":" + face; // returns a play that's not valid
}
}
}
return getBetterFallBack(lastBidCount, lastBidValue, totalDice); //if not smart bid, it bids the value from the getFallBackBid
}
private String getFallbackBid(int lastBidCount, int lastBidValue) {
int fallbackCount = lastBidCount;
int fallbackValue = lastBidValue + 1;
if (fallbackValue > MAX_FACE) { // if the value exceeds 6, the count increases
fallbackCount++;
fallbackValue = 2; //resets the face value to 2
}
return fallbackCount > 0 && fallbackValue > 0 ? "play:" + fallbackCount + ":" + fallbackValue : "play:bs";
}
private String getBetterFallBack(int lastBidCount, int lastBidValue, int totalDice) {
int nextCount = lastBidCount;
int nextValue = lastBidValue + 1;
if (nextValue > MAX_FACE) {
nextCount++;
nextValue = 2;
}
nextCount = Math.min(nextCount, totalDice);
if (nextCount <= totalDice) {
return "play:" + nextCount + ":" + nextValue;
} else {
return "play:bs";
}
}
private boolean shouldBluff(int myDice[], int currentBidCount, int currentBidValue, int totalDice) {
int wildcard = 0;
int sameFace = 0;
for (int die : myDice) {
if (die == 1) wildcard++; // counts wildcards (1's)
if (die == currentBidValue) sameFace ++; //checks if there is a matching face and adds it to sameFace
}
int knownCount = wildcard + sameFace;
int unknownDice = totalDice - myDice.length;
double expected = knownCount + (unknownDice / 6.0);
double bluffPercentage = BLUFF_CHANCE; //starts with the base bluff percentage chance
if (myDice.length <= 2) {
bluffPercentage += 0.15; //increases buff percent if I'm low on dices
}
if (expected >= currentBidCount - 1) return false; //but if true, I don't bluff
return random.nextDouble() < bluffPercentage;
}
private boolean isValid(int newCount, int newValue, int oldCount, int oldValue) {
return (newCount > oldCount) || (newCount == oldCount && newValue > oldValue); //makes sure bid is higher than last
}
private void processMessage(String message) throws IOException{
String[] parts = message.split(":");
int totalDice = Integer.parseInt(parts[1]);
int totalPlayers = Integer.parseInt(parts[2]);
int currentBidCount = Integer.parseInt(parts[3]);
int currentBidValue = Integer.parseInt(parts[4]); //parses the play message so I get the info for the bid count and value
int count[] = new int[MAX_FACE + 1];
for (int die : numDice) {
count[die]++;
}
if (currentBidCount == 0) {
makeInitialBid(count); //makes the first bid
} else {
handleCurrentBid(totalDice, currentBidCount, currentBidValue, count); //responds to the current bid
}
}
private void makeInitialBid(int[] count) throws IOException {
int bestFace = 2; //starts checking at 2 since 1 is invalid
int maxCount = count[2] + count[1]; //counts of face + wildcards
for (int i = 3; i <= 6; i++) {
int total = count[i] + count[1];
if (total > maxCount) {
maxCount = total;
bestFace = i;
}
}
int bidCount = Math.max(DEFAULT_BID, maxCount);
bidCount = Math.min(bidCount, numDice.length); //limits the starting bid to the amt of dices I have
write("play:" + bidCount + ":" + bestFace); //bids the best face
}
private void handleCurrentBid(int totalDice, int currentBidCount, int currentBidValue, int[] count) throws IOException{
if (shouldBluff(numDice, currentBidCount, currentBidValue, totalDice)) {
bluffBid(currentBidCount, currentBidValue); //trys to bluff
} else if(shouldChallengeBS(currentBidCount, currentBidValue, totalDice)) {
challengeBS(); //calls BS
} else {
makeSmartBid(totalDice, currentBidCount, currentBidValue, count); //make a calculated bid
}
}
private void bluffBid(int currentBidCount, int currentBidValue) throws IOException{
int nextBidCount = currentBidCount + 1;
int nextBidValue = (currentBidValue == 6) ? 2 : currentBidValue + 1; // Raising the face value as well
if (nextBidValue > 6) {
nextBidCount++;
nextBidValue = 2;
}
if (nextBidCount > numDice.length) {
write("play:bs");
return;
}
if (isValid(nextBidCount, nextBidValue, currentBidCount, currentBidValue)) {
write("play:" + nextBidCount + ":" + nextBidValue); //sends a bluff bid
} else {
write("play:bs"); //calls BS if there is no valid bluff
}
}
private void challengeBS() throws IOException{
write("play:bs"); //calls BS on opponents bid
}
private void makeSmartBid(int totalDice, int currentBidCount, int currentBidValue, int[] count) throws IOException{
String nextBid = getNextSmartBid(totalDice, currentBidCount + 1, currentBidValue, count, numDice.length, currentBidCount, currentBidValue);
write(nextBid); //sends a smart calculated bid
}
private boolean shouldChallengeBS(int currentBidCount, int currentBidValue, int totalDice) {
double expected = totalDice / 6.0 + (currentBidValue == 1 ? totalDice / 6.0: 0);
// If the current bid seems too high compared to what we expect, challenge it
return currentBidCount > expected + 2; // Challenges if the bid is suspiciously high
}
private void newRound(String message) {
String diceValues = message.split(":")[1];
System.out.println("My dice: " + diceValues);
numDice = new int[diceValues.length()];
for (int i = 0; i < diceValues.length(); i++) {
numDice[i] = Character.getNumericValue(diceValues.charAt(i));
}
}
public void run() throws IOException {
numDice = new int[0]; // Create array for the values of the dice
while (true) {
String message = read();
System.out.println("Received:" + message);
if (message.startsWith("login")) { // If I get a message that says login, it writes my githubID and avatar name
write(githubId + ":" + avatarName);
} else if (message.startsWith("newround:")) { // If I get newround, it splits the message so [1] is my dice values
newRound(message);
} else if (message.startsWith("play:")) {
processMessage(message);
} else if(message.startsWith("status:")) {
System.out.println("Status update: " + message);
} else if (message.startsWith("done")) {
System.out.println("Game over: " + message);
break;
}
}
socket.close();
}
public static void main(String[] args) {
try {
if (args.length < 2) { // Ensures there is a valid input
System.out.println("ERROR: Enter an IP address and IpPort number");
return;
}
String IpAddress = args[0];
int IpPort = Integer.parseInt(args[1]);
DiceClient me = new DiceClient(IpAddress, IpPort);
me.run();
} catch (IOException ioe) {
System.err.println("IO Exception: " + ioe);
ioe.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ERROR: No input");
} catch (IllegalArgumentException e) {
System.out.println("ERROR: Not a valid IP address or IpPort");
}
}
}