mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 15:16:33 -08:00
Classes in own files
This commit is contained in:
1
09_Battle/java/.gitignore
vendored
Normal file
1
09_Battle/java/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*~
|
||||||
@@ -144,199 +144,4 @@ public class Battle {
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Ship {
|
|
||||||
public static final int ORIENT_E=0;
|
|
||||||
public static final int ORIENT_SE=1;
|
|
||||||
public static final int ORIENT_S=2;
|
|
||||||
public static final int ORIENT_SW=3;
|
|
||||||
|
|
||||||
private int id;
|
|
||||||
private int size;
|
|
||||||
private String type;
|
|
||||||
private boolean placed;
|
|
||||||
private boolean sunk;
|
|
||||||
private ArrayList<Boolean> hits;
|
|
||||||
|
|
||||||
private int startX;
|
|
||||||
private int startY;
|
|
||||||
private int orientX;
|
|
||||||
private int orientY;
|
|
||||||
|
|
||||||
public Ship(int i, String name, int sz) {
|
|
||||||
id = i; type = name; size = sz;
|
|
||||||
sunk = false; placed = false;
|
|
||||||
hits = new ArrayList<>(Collections.nCopies(size, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int id() { return id; }
|
|
||||||
public int size() { return size; }
|
|
||||||
|
|
||||||
public void hit(int x, int y) {
|
|
||||||
int offset;
|
|
||||||
if (orientX != 0) {
|
|
||||||
offset = (x - startX) / orientX;
|
|
||||||
} else {
|
|
||||||
offset = (y - startY) / orientY;
|
|
||||||
}
|
|
||||||
hits.set(offset, true);
|
|
||||||
|
|
||||||
sunk = hits.stream().allMatch(Predicate.isEqual(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSunk() { return sunk; }
|
|
||||||
|
|
||||||
public boolean wasHit(int x, int y) {
|
|
||||||
int offset;
|
|
||||||
if (orientX != 0) {
|
|
||||||
offset = (x - startX) / orientX;
|
|
||||||
} else {
|
|
||||||
offset = (y - startY) / orientY;
|
|
||||||
}
|
|
||||||
return hits.get(offset);
|
|
||||||
};
|
|
||||||
|
|
||||||
public void placeRandom(Sea s) {
|
|
||||||
Random random = new Random();
|
|
||||||
for (int tries = 0 ; tries < 1000 ; ++tries) {
|
|
||||||
int x = random.nextInt(s.size());
|
|
||||||
int y = random.nextInt(s.size());
|
|
||||||
int orient = random.nextInt(4);
|
|
||||||
|
|
||||||
if (place(s, x, y, orient)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("Could not place any more ships");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean extendShip(Sea s, int fromX, int fromY, int toX, int toY) {
|
|
||||||
if (!s.isEmpty(toX, toY)) return false; // no space
|
|
||||||
if ((fromX == toX)||(fromY == toY)) return true; // horizontal or vertical
|
|
||||||
|
|
||||||
// we can extend the ship without colliding, but we are going diagonally
|
|
||||||
// and it should not be possible for two ships to cross each other on
|
|
||||||
// opposite diagonals.
|
|
||||||
|
|
||||||
// check the two tiles that would cross us here - if either is empty, we are OK
|
|
||||||
// if they both contain different ships, we are OK
|
|
||||||
// but if they both contain the same ship, we are crossing!
|
|
||||||
int corner1 = s.get(fromX, toY);
|
|
||||||
int corner2 = s.get(toX, fromY);
|
|
||||||
if ((corner1 == 0) || (corner1 != corner2)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean place(Sea s, int x, int y, int orient) {
|
|
||||||
if (placed) {
|
|
||||||
throw new RuntimeException("Program error - placed ship " + id + " twice");
|
|
||||||
}
|
|
||||||
switch(orient) {
|
|
||||||
case ORIENT_E:
|
|
||||||
orientX = 1; orientY = 0;
|
|
||||||
break;
|
|
||||||
case ORIENT_SE:
|
|
||||||
orientX = 1; orientY = 1;
|
|
||||||
break;
|
|
||||||
case ORIENT_S:
|
|
||||||
orientX = 0; orientY = 1;
|
|
||||||
break;
|
|
||||||
case ORIENT_SW:
|
|
||||||
orientX = -1; orientY = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Invalid orientation " + orient);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!s.isEmpty(x, y)) return false;
|
|
||||||
startX = x; startY = y;
|
|
||||||
int tilesPlaced = 1;
|
|
||||||
int nextX = startX;
|
|
||||||
int nextY = startY;
|
|
||||||
while (tilesPlaced < size) {
|
|
||||||
if (extendShip(s, nextX, nextY, nextX + orientX, nextY + orientY)) {
|
|
||||||
tilesPlaced += 1;
|
|
||||||
nextX = nextX + orientX;
|
|
||||||
nextY = nextY + orientY;
|
|
||||||
} else {
|
|
||||||
int backX = startX - orientX;
|
|
||||||
int backY = startY - orientY;
|
|
||||||
|
|
||||||
if (extendShip(s, startX, startY, backX, backY)) {
|
|
||||||
tilesPlaced +=1;
|
|
||||||
startX = backX;
|
|
||||||
startY = backY;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
int sx = startX + i * orientX;
|
|
||||||
int sy = startY + i * orientY;
|
|
||||||
s.set(sx, sy, id);
|
|
||||||
}
|
|
||||||
placed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Sea {
|
|
||||||
private int tiles[];
|
|
||||||
private boolean hits[];
|
|
||||||
|
|
||||||
private int size;
|
|
||||||
public Sea(int make_size) {
|
|
||||||
size = make_size;
|
|
||||||
tiles = new int[size*size];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() { return size; }
|
|
||||||
|
|
||||||
public String encodedDump() {
|
|
||||||
StringBuilder out = new StringBuilder();
|
|
||||||
for (int x = 0; x < size; ++x) {
|
|
||||||
for (int y = 0; y < size; ++y)
|
|
||||||
out.append(Integer.toString(get(x, y)));
|
|
||||||
out.append('\n');
|
|
||||||
}
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return true if x,y is in the sea and empty
|
|
||||||
* return false if x,y is occupied or is out of range
|
|
||||||
*/
|
|
||||||
public boolean isEmpty(int x, int y) {
|
|
||||||
if ((x<0)||(x>=size)||(y<0)||(y>=size)) return false;
|
|
||||||
return (get(x,y) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return the ship number, or zero if no ship */
|
|
||||||
public int get(int x, int y) {
|
|
||||||
return tiles[index(x,y)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(int x, int y, int value) {
|
|
||||||
tiles[index(x, y)] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int shipHit(int x, int y) {
|
|
||||||
if (hits[index(x,y)]) return get(x, y);
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void recordHit(int x, int y) {
|
|
||||||
hits[index(x, y)] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int index(int x, int y) {
|
|
||||||
if ((x < 0) || (x >= size))
|
|
||||||
throw new ArrayIndexOutOfBoundsException("Program error: x cannot be " + x);
|
|
||||||
if ((y < 0) || (y >= size))
|
|
||||||
throw new ArrayIndexOutOfBoundsException("Program error: y cannot be " + y);
|
|
||||||
|
|
||||||
return y*size + x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
58
09_Battle/java/Input.java
Normal file
58
09_Battle/java/Input.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
|
public class Input {
|
||||||
|
private BufferedReader reader;
|
||||||
|
private NumberFormat parser;
|
||||||
|
private int scale;
|
||||||
|
private boolean isQuit;
|
||||||
|
private int[] coords;
|
||||||
|
|
||||||
|
public Input(int seaSize) {
|
||||||
|
scale = seaSize;
|
||||||
|
reader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
parser = NumberFormat.getIntegerInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean readCoordinates() throws IOException {
|
||||||
|
while (true) {
|
||||||
|
System.out.print("\nTarget x,y\n> ");
|
||||||
|
String inputLine = reader.readLine();
|
||||||
|
if (inputLine == null) {
|
||||||
|
System.out.println("Game quit\n");
|
||||||
|
isQuit = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] fields = inputLine.split(",");
|
||||||
|
if (fields.length != 2) {
|
||||||
|
System.out.println("Need two coordinates separated by ','");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
coords = new int[2];
|
||||||
|
boolean error = false;
|
||||||
|
try {
|
||||||
|
for (int c = 0 ; c < 2; ++c ) {
|
||||||
|
int val = Integer.parseInt(fields[c].strip());
|
||||||
|
if ((val < 1) || (val > scale)) {
|
||||||
|
System.out.println("Coordinates must be from 1 to " + scale);
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
coords[c] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ne) {
|
||||||
|
System.out.println("Coordinates must be numbers");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
if (!error) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int x() { return coords[0]; }
|
||||||
|
public int y() { return coords[1]; }
|
||||||
|
}
|
||||||
57
09_Battle/java/Sea.java
Normal file
57
09_Battle/java/Sea.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
class Sea {
|
||||||
|
private int tiles[];
|
||||||
|
private boolean hits[];
|
||||||
|
|
||||||
|
private int size;
|
||||||
|
public Sea(int make_size) {
|
||||||
|
size = make_size;
|
||||||
|
tiles = new int[size*size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() { return size; }
|
||||||
|
|
||||||
|
public String encodedDump() {
|
||||||
|
StringBuilder out = new StringBuilder();
|
||||||
|
for (int x = 0; x < size; ++x) {
|
||||||
|
for (int y = 0; y < size; ++y)
|
||||||
|
out.append(Integer.toString(get(x, y)));
|
||||||
|
out.append('\n');
|
||||||
|
}
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return true if x,y is in the sea and empty
|
||||||
|
* return false if x,y is occupied or is out of range
|
||||||
|
*/
|
||||||
|
public boolean isEmpty(int x, int y) {
|
||||||
|
if ((x<0)||(x>=size)||(y<0)||(y>=size)) return false;
|
||||||
|
return (get(x,y) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return the ship number, or zero if no ship */
|
||||||
|
public int get(int x, int y) {
|
||||||
|
return tiles[index(x,y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int x, int y, int value) {
|
||||||
|
tiles[index(x, y)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int shipHit(int x, int y) {
|
||||||
|
if (hits[index(x,y)]) return get(x, y);
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordHit(int x, int y) {
|
||||||
|
hits[index(x, y)] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int index(int x, int y) {
|
||||||
|
if ((x < 0) || (x >= size))
|
||||||
|
throw new ArrayIndexOutOfBoundsException("Program error: x cannot be " + x);
|
||||||
|
if ((y < 0) || (y >= size))
|
||||||
|
throw new ArrayIndexOutOfBoundsException("Program error: y cannot be " + y);
|
||||||
|
|
||||||
|
return y*size + x;
|
||||||
|
}
|
||||||
|
}
|
||||||
142
09_Battle/java/Ship.java
Normal file
142
09_Battle/java/Ship.java
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
class Ship {
|
||||||
|
public static final int ORIENT_E=0;
|
||||||
|
public static final int ORIENT_SE=1;
|
||||||
|
public static final int ORIENT_S=2;
|
||||||
|
public static final int ORIENT_SW=3;
|
||||||
|
|
||||||
|
private int id;
|
||||||
|
private int size;
|
||||||
|
private String type;
|
||||||
|
private boolean placed;
|
||||||
|
private boolean sunk;
|
||||||
|
private ArrayList<Boolean> hits;
|
||||||
|
|
||||||
|
private int startX;
|
||||||
|
private int startY;
|
||||||
|
private int orientX;
|
||||||
|
private int orientY;
|
||||||
|
|
||||||
|
public Ship(int i, String name, int sz) {
|
||||||
|
id = i; type = name; size = sz;
|
||||||
|
sunk = false; placed = false;
|
||||||
|
hits = new ArrayList<>(Collections.nCopies(size, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int id() { return id; }
|
||||||
|
public int size() { return size; }
|
||||||
|
|
||||||
|
public void hit(int x, int y) {
|
||||||
|
int offset;
|
||||||
|
if (orientX != 0) {
|
||||||
|
offset = (x - startX) / orientX;
|
||||||
|
} else {
|
||||||
|
offset = (y - startY) / orientY;
|
||||||
|
}
|
||||||
|
hits.set(offset, true);
|
||||||
|
|
||||||
|
sunk = hits.stream().allMatch(Predicate.isEqual(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSunk() { return sunk; }
|
||||||
|
|
||||||
|
public boolean wasHit(int x, int y) {
|
||||||
|
int offset;
|
||||||
|
if (orientX != 0) {
|
||||||
|
offset = (x - startX) / orientX;
|
||||||
|
} else {
|
||||||
|
offset = (y - startY) / orientY;
|
||||||
|
}
|
||||||
|
return hits.get(offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
public void placeRandom(Sea s) {
|
||||||
|
Random random = new Random();
|
||||||
|
for (int tries = 0 ; tries < 1000 ; ++tries) {
|
||||||
|
int x = random.nextInt(s.size());
|
||||||
|
int y = random.nextInt(s.size());
|
||||||
|
int orient = random.nextInt(4);
|
||||||
|
|
||||||
|
if (place(s, x, y, orient)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Could not place any more ships");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean extendShip(Sea s, int fromX, int fromY, int toX, int toY) {
|
||||||
|
if (!s.isEmpty(toX, toY)) return false; // no space
|
||||||
|
if ((fromX == toX)||(fromY == toY)) return true; // horizontal or vertical
|
||||||
|
|
||||||
|
// we can extend the ship without colliding, but we are going diagonally
|
||||||
|
// and it should not be possible for two ships to cross each other on
|
||||||
|
// opposite diagonals.
|
||||||
|
|
||||||
|
// check the two tiles that would cross us here - if either is empty, we are OK
|
||||||
|
// if they both contain different ships, we are OK
|
||||||
|
// but if they both contain the same ship, we are crossing!
|
||||||
|
int corner1 = s.get(fromX, toY);
|
||||||
|
int corner2 = s.get(toX, fromY);
|
||||||
|
if ((corner1 == 0) || (corner1 != corner2)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean place(Sea s, int x, int y, int orient) {
|
||||||
|
if (placed) {
|
||||||
|
throw new RuntimeException("Program error - placed ship " + id + " twice");
|
||||||
|
}
|
||||||
|
switch(orient) {
|
||||||
|
case ORIENT_E:
|
||||||
|
orientX = 1; orientY = 0;
|
||||||
|
break;
|
||||||
|
case ORIENT_SE:
|
||||||
|
orientX = 1; orientY = 1;
|
||||||
|
break;
|
||||||
|
case ORIENT_S:
|
||||||
|
orientX = 0; orientY = 1;
|
||||||
|
break;
|
||||||
|
case ORIENT_SW:
|
||||||
|
orientX = -1; orientY = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Invalid orientation " + orient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s.isEmpty(x, y)) return false;
|
||||||
|
startX = x; startY = y;
|
||||||
|
int tilesPlaced = 1;
|
||||||
|
int nextX = startX;
|
||||||
|
int nextY = startY;
|
||||||
|
while (tilesPlaced < size) {
|
||||||
|
if (extendShip(s, nextX, nextY, nextX + orientX, nextY + orientY)) {
|
||||||
|
tilesPlaced += 1;
|
||||||
|
nextX = nextX + orientX;
|
||||||
|
nextY = nextY + orientY;
|
||||||
|
} else {
|
||||||
|
int backX = startX - orientX;
|
||||||
|
int backY = startY - orientY;
|
||||||
|
|
||||||
|
if (extendShip(s, startX, startY, backX, backY)) {
|
||||||
|
tilesPlaced +=1;
|
||||||
|
startX = backX;
|
||||||
|
startY = backY;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int sx = startX + i * orientX;
|
||||||
|
int sy = startY + i * orientY;
|
||||||
|
s.set(sx, sy, id);
|
||||||
|
}
|
||||||
|
placed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user