Merge branch 'coding-horror:main' into csharp-56-life-for-two

This commit is contained in:
Andrew Cooper
2022-09-13 08:37:38 +10:00
committed by GitHub
105 changed files with 2470 additions and 5 deletions

View File

@@ -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.

View 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;
}
}
}

View 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()