mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-01-07 02:24:33 -08:00
Merge branch 'coding-horror:main' into csharp-56-life-for-two
This commit is contained in:
@@ -6,6 +6,7 @@ There are two players; the game is played on a 5x5 board and each player has a s
|
||||
|
||||
The # and * are regarded as the same except when deciding whether to generate a live cell. An empty cell having two `#` and one `*` for neighbors will generate a `#`, i.e. the live cell generated belongs to the player who has the majority of the 3 live cells surrounding the empty cell where life is to be generated, for example:
|
||||
|
||||
```
|
||||
| | 1 | 2 | 3 | 4 | 5 |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| 1 | | | | | |
|
||||
@@ -13,9 +14,10 @@ The # and * are regarded as the same except when deciding whether to generate a
|
||||
| 3 | | | | # | |
|
||||
| 4 | | | # | | |
|
||||
| 5 | | | | | |
|
||||
```
|
||||
|
||||
A new cell will be generated at (3,3) which will be a `#` since there are two `#` and one `*` surrounding. The board will then become:
|
||||
|
||||
```
|
||||
| | 1 | 2 | 3 | 4 | 5 |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| 1 | | | | | |
|
||||
@@ -23,7 +25,7 @@ A new cell will be generated at (3,3) which will be a `#` since there are two `#
|
||||
| 3 | | | # | # | |
|
||||
| 4 | | | | | |
|
||||
| 5 | | | | | |
|
||||
|
||||
```
|
||||
On the first most each player positions 3 pieces of life on the board by typing in the co-ordinates of the pieces. (In the event of the same cell being chosen by both players that cell is left empty.)
|
||||
|
||||
The board is then adjusted to the next generation and printed out.
|
||||
|
||||
305
56_Life_for_Two/java/LifeForTwo.java
Normal file
305
56_Life_for_Two/java/LifeForTwo.java
Normal file
@@ -0,0 +1,305 @@
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* Life for Two
|
||||
* <p>
|
||||
* The original BASIC program uses a grid with an extras border of cells all around,
|
||||
* probably to simplify calculations and manipulations. This java program has the exact
|
||||
* grid size and instead uses boundary check conditions in the logic.
|
||||
* <p>
|
||||
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
|
||||
*/
|
||||
public class LifeForTwo {
|
||||
|
||||
final static int GRID_SIZE = 5;
|
||||
|
||||
//Pair of offset which when added to the current cell's coordinates,
|
||||
// give the coordinates of the neighbours
|
||||
final static int[] neighbourCellOffsets = {
|
||||
-1, 0,
|
||||
1, 0,
|
||||
0, -1,
|
||||
0, 1,
|
||||
-1, -1,
|
||||
1, -1,
|
||||
-1, 1,
|
||||
1, 1
|
||||
};
|
||||
|
||||
//The best term that I could come with to describe these numbers was 'masks'
|
||||
//They act like indicators to decide which player won the cell. The value is the score of the cell after all the
|
||||
// generation calculations.
|
||||
final static List<Integer> maskPlayer1 = List.of(3, 102, 103, 120, 130, 121, 112, 111, 12);
|
||||
final static List<Integer> maskPlayer2 = List.of(21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012);
|
||||
|
||||
public static void main(String[] args) {
|
||||
printIntro();
|
||||
Scanner scan = new Scanner(System.in);
|
||||
scan.useDelimiter("\\D");
|
||||
|
||||
int[][] grid = new int[GRID_SIZE][GRID_SIZE];
|
||||
|
||||
initializeGrid(grid);
|
||||
|
||||
//Read the initial 3 moves for each player
|
||||
for (int b = 1; b <= 2; b++) {
|
||||
System.out.printf("\nPLAYER %d - 3 LIVE PIECES.%n", b);
|
||||
for (int k1 = 1; k1 <= 3; k1++) {
|
||||
var player1Coordinates = readUntilValidCoordinates(scan, grid);
|
||||
grid[player1Coordinates.x - 1][player1Coordinates.y - 1] = (b == 1 ? 3 : 30);
|
||||
}
|
||||
}
|
||||
|
||||
printGrid(grid);
|
||||
|
||||
calculatePlayersScore(grid); //Convert 3, 30 to 100, 1000
|
||||
|
||||
resetGridForNextGen(grid);
|
||||
computeCellScoresForOneGen(grid);
|
||||
|
||||
var playerScores = calculatePlayersScore(grid);
|
||||
resetGridForNextGen(grid);
|
||||
|
||||
boolean gameOver = false;
|
||||
while (!gameOver) {
|
||||
printGrid(grid);
|
||||
if (playerScores.getPlayer1Score() == 0 && playerScores.getPlayer2Score() == 0) {
|
||||
System.out.println("\nA DRAW");
|
||||
gameOver = true;
|
||||
} else if (playerScores.getPlayer2Score() == 0) {
|
||||
System.out.println("\nPLAYER 1 IS THE WINNER");
|
||||
gameOver = true;
|
||||
} else if (playerScores.getPlayer1Score() == 0) {
|
||||
System.out.println("\nPLAYER 2 IS THE WINNER");
|
||||
gameOver = true;
|
||||
} else {
|
||||
System.out.print("PLAYER 1 ");
|
||||
Coordinate player1Move = readCoordinate(scan);
|
||||
System.out.print("PLAYER 2 ");
|
||||
Coordinate player2Move = readCoordinate(scan);
|
||||
if (!player1Move.equals(player2Move)) {
|
||||
grid[player1Move.x - 1][player1Move.y - 1] = 100;
|
||||
grid[player2Move.x - 1][player2Move.y - 1] = 1000;
|
||||
}
|
||||
//In the original, B is assigned 99 when both players choose the same cell
|
||||
//and that is used to control the flow
|
||||
computeCellScoresForOneGen(grid);
|
||||
playerScores = calculatePlayersScore(grid);
|
||||
resetGridForNextGen(grid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void initializeGrid(int[][] grid) {
|
||||
for (int[] row : grid) {
|
||||
Arrays.fill(row, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void computeCellScoresForOneGen(int[][] grid) {
|
||||
for (int i = 0; i < GRID_SIZE; i++) {
|
||||
for (int j = 0; j < GRID_SIZE; j++) {
|
||||
if (grid[i][j] >= 100) {
|
||||
calculateScoreForOccupiedCell(grid, i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Scores calculatePlayersScore(int[][] grid) {
|
||||
int m2 = 0;
|
||||
int m3 = 0;
|
||||
for (int i = 0; i < GRID_SIZE; i++) {
|
||||
for (int j = 0; j < GRID_SIZE; j++) {
|
||||
if (grid[i][j] < 3) {
|
||||
grid[i][j] = 0;
|
||||
} else {
|
||||
if (maskPlayer1.contains(grid[i][j])) {
|
||||
m2++;
|
||||
} else if (maskPlayer2.contains(grid[i][j])) {
|
||||
m3++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Scores(m2, m3);
|
||||
}
|
||||
|
||||
private static void resetGridForNextGen(int[][] grid) {
|
||||
for (int i = 0; i < GRID_SIZE; i++) {
|
||||
for (int j = 0; j < GRID_SIZE; j++) {
|
||||
if (grid[i][j] < 3) {
|
||||
grid[i][j] = 0;
|
||||
} else {
|
||||
if (maskPlayer1.contains(grid[i][j])) {
|
||||
grid[i][j] = 100;
|
||||
} else if (maskPlayer2.contains(grid[i][j])) {
|
||||
grid[i][j] = 1000;
|
||||
} else {
|
||||
grid[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void calculateScoreForOccupiedCell(int[][] grid, int i, int j) {
|
||||
var b = 1;
|
||||
if (grid[i][j] > 999) {
|
||||
b = 10;
|
||||
}
|
||||
for (int k = 0; k < 15; k += 2) {
|
||||
//check bounds
|
||||
var neighbourX = i + neighbourCellOffsets[k];
|
||||
var neighbourY = j + neighbourCellOffsets[k + 1];
|
||||
if (neighbourX >= 0 && neighbourX < GRID_SIZE &&
|
||||
neighbourY >= 0 && neighbourY < GRID_SIZE) {
|
||||
grid[neighbourX][neighbourY] = grid[neighbourX][neighbourY] + b;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void printGrid(int[][] grid) {
|
||||
System.out.println();
|
||||
printRowEdge();
|
||||
System.out.println();
|
||||
for (int i = 0; i < grid.length; i++) {
|
||||
System.out.printf("%d ", i + 1);
|
||||
for (int j = 0; j < grid[i].length; j++) {
|
||||
System.out.printf(" %c ", mapChar(grid[i][j]));
|
||||
}
|
||||
System.out.printf(" %d", i + 1);
|
||||
System.out.println();
|
||||
}
|
||||
printRowEdge();
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private static void printRowEdge() {
|
||||
System.out.print("0 ");
|
||||
IntStream.range(1, GRID_SIZE + 1).forEach(i -> System.out.printf(" %s ", i));
|
||||
System.out.print(" 0");
|
||||
}
|
||||
|
||||
private static char mapChar(int i) {
|
||||
if (i == 3 || i == 100) {
|
||||
return '*';
|
||||
}
|
||||
if (i == 30 || i == 1000) {
|
||||
return '#';
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
|
||||
private static Coordinate readUntilValidCoordinates(Scanner scanner, int[][] grid) {
|
||||
boolean coordinateInRange = false;
|
||||
Coordinate coordinate = null;
|
||||
while (!coordinateInRange) {
|
||||
coordinate = readCoordinate(scanner);
|
||||
if (coordinate.x <= 0 || coordinate.x > GRID_SIZE
|
||||
|| coordinate.y <= 0 || coordinate.y > GRID_SIZE
|
||||
|| grid[coordinate.x - 1][coordinate.y - 1] != 0) {
|
||||
System.out.println("ILLEGAL COORDS. RETYPE");
|
||||
} else {
|
||||
coordinateInRange = true;
|
||||
}
|
||||
}
|
||||
return coordinate;
|
||||
}
|
||||
|
||||
private static Coordinate readCoordinate(Scanner scanner) {
|
||||
Coordinate coordinate = null;
|
||||
int x, y;
|
||||
boolean valid = false;
|
||||
|
||||
System.out.println("X,Y");
|
||||
System.out.print("XXXXXX\r");
|
||||
System.out.print("$$$$$$\r");
|
||||
System.out.print("&&&&&&\r");
|
||||
|
||||
while (!valid) {
|
||||
try {
|
||||
System.out.print("? ");
|
||||
y = scanner.nextInt();
|
||||
x = scanner.nextInt();
|
||||
valid = true;
|
||||
coordinate = new Coordinate(x, y);
|
||||
} catch (InputMismatchException e) {
|
||||
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
|
||||
valid = false;
|
||||
} finally {
|
||||
scanner.nextLine();
|
||||
}
|
||||
}
|
||||
return coordinate;
|
||||
}
|
||||
|
||||
private static void printIntro() {
|
||||
System.out.println(" LIFE2");
|
||||
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
System.out.println("\n\n");
|
||||
|
||||
System.out.println("\tU.B. LIFE GAME");
|
||||
}
|
||||
|
||||
private static class Coordinate {
|
||||
private final int x, y;
|
||||
|
||||
public Coordinate(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Coordinate{" +
|
||||
"x=" + x +
|
||||
", y=" + y +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Coordinate that = (Coordinate) o;
|
||||
return x == that.x && y == that.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Scores {
|
||||
private final int player1Score;
|
||||
private final int player2Score;
|
||||
|
||||
public Scores(int player1Score, int player2Score) {
|
||||
this.player1Score = player1Score;
|
||||
this.player2Score = player2Score;
|
||||
}
|
||||
|
||||
public int getPlayer1Score() {
|
||||
return player1Score;
|
||||
}
|
||||
|
||||
public int getPlayer2Score() {
|
||||
return player2Score;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
155
56_Life_for_Two/python/life_for_two.py
Normal file
155
56_Life_for_Two/python/life_for_two.py
Normal file
@@ -0,0 +1,155 @@
|
||||
'''
|
||||
LIFE FOR TWO
|
||||
|
||||
Competitive Game of Life (two or more players).
|
||||
|
||||
Ported by Sajid Sarker (2022).
|
||||
'''
|
||||
# Global Variable Initialisation
|
||||
gn = []
|
||||
gx = []
|
||||
gy = []
|
||||
gk = [0, 3, 102, 103, 120, 130, 121, 112, 111, 12, 21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012]
|
||||
ga = [0, -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, -1, -1, 1, 1, 1]
|
||||
m2 = 0
|
||||
m3 = 0
|
||||
|
||||
# Initialise the board
|
||||
for j in range(6):
|
||||
gn.append([])
|
||||
for k in range(6):
|
||||
gn[j].append(0)
|
||||
|
||||
for i in range(3):
|
||||
gx.append(0)
|
||||
gy.append(0)
|
||||
|
||||
# Helper Functions
|
||||
def tab(number) -> str:
|
||||
t = ""
|
||||
while len(t) < number:
|
||||
t += " "
|
||||
return t
|
||||
|
||||
def display_header() -> None:
|
||||
print("{}LIFE2".format(tab(33)))
|
||||
print("{}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n".format(tab(15)))
|
||||
print("{}U.B. LIFE GAME".format(tab(10)))
|
||||
|
||||
# Board Functions
|
||||
def setup_board() -> None:
|
||||
# Players add symbols to initially setup the board
|
||||
for b in range(1, 3):
|
||||
p1 = 3 if b != 2 else 30
|
||||
print("\nPLAYER {} - 3 LIVE PIECES.".format(b))
|
||||
for k1 in range(1, 4):
|
||||
query_player(b)
|
||||
gn[gx[b]][gy[b]] = p1
|
||||
|
||||
def modify_board() -> None:
|
||||
# Players take turns to add symbols and modify the board
|
||||
for b in range(1, 3):
|
||||
print("PLAYER {} ".format(b))
|
||||
query_player(b)
|
||||
if b == 99:
|
||||
break
|
||||
if b <= 2:
|
||||
gn[gx[1]][gy[1]] = 100
|
||||
gn[gx[2]][gy[2]] = 1000
|
||||
|
||||
def simulate_board() -> None:
|
||||
# Simulate the board for one step
|
||||
for j in range(1, 6):
|
||||
for k in range(1, 6):
|
||||
if gn[j][k] > 99:
|
||||
b = 1 if gn[j][k] <= 999 else 10
|
||||
for o1 in range(1, 16, 2):
|
||||
gn[j + ga[o1] - 1][k + ga[o1 + 1] - 1] = gn[j + ga[o1] - 1][k + ga[o1 + 1] - 1] + b
|
||||
#gn[j + ga[o1]][k + ga[o1 + 1]] = gn[j + ga[o1]][k + ga[o1 + 1]] + b
|
||||
|
||||
def display_board() -> None:
|
||||
# Draws the board with all symbols
|
||||
m2, m3 = 0, 0
|
||||
for j in range(7):
|
||||
print("")
|
||||
for k in range(7):
|
||||
if j == 0 or j == 6:
|
||||
if k != 6:
|
||||
print(" " + str(k) + " ", end="")
|
||||
else:
|
||||
print(" 0 ", end="")
|
||||
elif k == 0 or k == 6:
|
||||
if j != 6:
|
||||
print(" " + str(j) + " ", end="")
|
||||
else:
|
||||
print(" 0\n")
|
||||
else:
|
||||
if gn[j][k] < 3:
|
||||
gn[j][k] = 0
|
||||
print(" ", end="")
|
||||
else:
|
||||
for o1 in range(1, 19):
|
||||
if gn[j][k] == gk[o1]:
|
||||
break
|
||||
if o1 <= 18:
|
||||
if o1 > 9:
|
||||
gn[j][k] = 1000
|
||||
m3 += 1
|
||||
print(" # ", end="")
|
||||
else:
|
||||
gn[j][k] = 100
|
||||
m2 += 1
|
||||
print(" * ", end="")
|
||||
else:
|
||||
gn[j][k] = 0
|
||||
print(" ", end="")
|
||||
|
||||
# Player Functions
|
||||
def query_player(b) -> None:
|
||||
# Query player for symbol placement coordinates
|
||||
while True:
|
||||
print("X,Y\nXXXXXX\n$$$$$$\n&&&&&&")
|
||||
a_ = input("??")
|
||||
b_ = input("???")
|
||||
x_ = [int(num) for num in a_.split() if num.isdigit()]
|
||||
y_ = [int(num) for num in b_.split() if num.isdigit()]
|
||||
x_ = [0] if len(x_) == 0 else x_
|
||||
y_ = [0] if len(y_) == 0 else y_
|
||||
gx[b] = y_[0]
|
||||
gy[b] = x_[0]
|
||||
if gx[b] in range(1, 6) and gy[b] in range(1, 6) and gn[gx[b]][gy[b]] == 0:
|
||||
break
|
||||
print("ILLEGAL COORDS. RETYPE")
|
||||
if b != 1:
|
||||
if gx[1] == gx[2] and gy[1] == gy[2]:
|
||||
print("SAME COORD. SET TO 0")
|
||||
gn[gx[b] + 1][gy[b] + 1] = 0
|
||||
b = 99
|
||||
|
||||
# Game Functions
|
||||
def check_winner(m2, m3) -> None:
|
||||
# Check if the game has been won
|
||||
if m2 == 0 and m3 == 0:
|
||||
print("\nA DRAW\n")
|
||||
return
|
||||
if m3 == 0:
|
||||
print("\nPLAYER 1 IS THE WINNER\n")
|
||||
return
|
||||
if m2 == 0:
|
||||
print("\nPLAYER 2 IS THE WINNER\n")
|
||||
return
|
||||
|
||||
# Program Flow
|
||||
def main() -> None:
|
||||
display_header()
|
||||
setup_board()
|
||||
display_board()
|
||||
while True:
|
||||
print("\n")
|
||||
simulate_board()
|
||||
display_board()
|
||||
check_winner(m2, m3)
|
||||
modify_board()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user