mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-01-09 03:43:01 -08:00
Merge branch 'main' of https://github.com/coding-horror/basic-computer-games
This commit is contained in:
292
08 Batnum/java/src/BatNum.java
Normal file
292
08 Batnum/java/src/BatNum.java
Normal file
@@ -0,0 +1,292 @@
|
||||
import java.util.Arrays;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Game of BatNum
|
||||
* <p>
|
||||
* Based on the Basic game of BatNum here
|
||||
* https://github.com/coding-horror/basic-computer-games/blob/main/08%20Batnum/batnum.bas
|
||||
* <p>
|
||||
* Note: The idea was to create a version of the 1970's Basic game in Java, without introducing
|
||||
* new features - no additional text, error checking, etc has been added.
|
||||
*/
|
||||
public class BatNum {
|
||||
|
||||
private enum GAME_STATE {
|
||||
STARTING,
|
||||
START_GAME,
|
||||
CHOOSE_PILE_SIZE,
|
||||
SELECT_WIN_OPTION,
|
||||
CHOOSE_MIN_AND_MAX,
|
||||
SELECT_WHO_STARTS_FIRST,
|
||||
PLAYERS_TURN,
|
||||
COMPUTERS_TURN,
|
||||
ANNOUNCE_WINNER,
|
||||
GAME_OVER
|
||||
}
|
||||
|
||||
// Used for keyboard input
|
||||
private final Scanner kbScanner;
|
||||
|
||||
// Current game state
|
||||
private GAME_STATE gameState;
|
||||
|
||||
private int pileSize;
|
||||
|
||||
// How to win the game options
|
||||
enum WIN_OPTION {
|
||||
TAKE_LAST,
|
||||
AVOID_LAST
|
||||
}
|
||||
|
||||
// Tracking the winner
|
||||
enum WINNER {
|
||||
COMPUTER,
|
||||
PLAYER
|
||||
}
|
||||
|
||||
private WINNER winner;
|
||||
|
||||
private WIN_OPTION winOption;
|
||||
|
||||
private int minSelection;
|
||||
private int maxSelection;
|
||||
|
||||
// Used by computer for optimal move
|
||||
private int rangeOfRemovals;
|
||||
|
||||
public BatNum() {
|
||||
|
||||
gameState = GAME_STATE.STARTING;
|
||||
|
||||
// Initialise kb scanner
|
||||
kbScanner = new Scanner(System.in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main game loop
|
||||
*/
|
||||
public void play() {
|
||||
|
||||
do {
|
||||
switch (gameState) {
|
||||
|
||||
// Show an introduction and optional instructions the first time the game is played.
|
||||
case STARTING:
|
||||
intro();
|
||||
gameState = GAME_STATE.START_GAME;
|
||||
break;
|
||||
|
||||
// Start new game
|
||||
case START_GAME:
|
||||
gameState = GAME_STATE.CHOOSE_PILE_SIZE;
|
||||
break;
|
||||
|
||||
case CHOOSE_PILE_SIZE:
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
pileSize = displayTextAndGetNumber("ENTER PILE SIZE ");
|
||||
if (pileSize >= 1) {
|
||||
gameState = GAME_STATE.SELECT_WIN_OPTION;
|
||||
}
|
||||
break;
|
||||
|
||||
case SELECT_WIN_OPTION:
|
||||
int winChoice = displayTextAndGetNumber("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ");
|
||||
if (winChoice == 1) {
|
||||
winOption = WIN_OPTION.TAKE_LAST;
|
||||
gameState = GAME_STATE.CHOOSE_MIN_AND_MAX;
|
||||
} else if (winChoice == 2) {
|
||||
winOption = WIN_OPTION.AVOID_LAST;
|
||||
gameState = GAME_STATE.CHOOSE_MIN_AND_MAX;
|
||||
}
|
||||
break;
|
||||
|
||||
case CHOOSE_MIN_AND_MAX:
|
||||
String range = displayTextAndGetInput("ENTER MIN AND MAX ");
|
||||
minSelection = getDelimitedValue(range, 0);
|
||||
maxSelection = getDelimitedValue(range, 1);
|
||||
if (maxSelection > minSelection && minSelection >= 1) {
|
||||
gameState = GAME_STATE.SELECT_WHO_STARTS_FIRST;
|
||||
}
|
||||
|
||||
// Used by computer in its turn
|
||||
rangeOfRemovals = minSelection + maxSelection;
|
||||
break;
|
||||
|
||||
case SELECT_WHO_STARTS_FIRST:
|
||||
int playFirstChoice = displayTextAndGetNumber("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ");
|
||||
if (playFirstChoice == 1) {
|
||||
gameState = GAME_STATE.COMPUTERS_TURN;
|
||||
} else if (playFirstChoice == 2) {
|
||||
gameState = GAME_STATE.PLAYERS_TURN;
|
||||
}
|
||||
break;
|
||||
|
||||
case PLAYERS_TURN:
|
||||
int playersMove = displayTextAndGetNumber("YOUR MOVE ");
|
||||
|
||||
if (playersMove == 0) {
|
||||
System.out.println("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.");
|
||||
winner = WINNER.COMPUTER;
|
||||
gameState = GAME_STATE.ANNOUNCE_WINNER;
|
||||
break;
|
||||
}
|
||||
|
||||
if (playersMove == pileSize && winOption == WIN_OPTION.AVOID_LAST) {
|
||||
winner = WINNER.COMPUTER;
|
||||
gameState = GAME_STATE.ANNOUNCE_WINNER;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if players move is with the min and max possible
|
||||
if (playersMove >= minSelection && playersMove <= maxSelection) {
|
||||
// Valid so reduce pileSize by amount player entered
|
||||
pileSize -= playersMove;
|
||||
|
||||
// Did this move result in there being no more objects on pile?
|
||||
if (pileSize == 0) {
|
||||
// Was the game setup so the winner was whoever took the last object
|
||||
if (winOption == WIN_OPTION.TAKE_LAST) {
|
||||
// Player won
|
||||
winner = WINNER.PLAYER;
|
||||
} else {
|
||||
// Computer one
|
||||
winner = WINNER.COMPUTER;
|
||||
}
|
||||
gameState = GAME_STATE.ANNOUNCE_WINNER;
|
||||
} else {
|
||||
// There are still items left.
|
||||
gameState = GAME_STATE.COMPUTERS_TURN;
|
||||
}
|
||||
} else {
|
||||
// Invalid move
|
||||
System.out.println("ILLEGAL MOVE, REENTER IT ");
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPUTERS_TURN:
|
||||
int pileSizeLeft = pileSize;
|
||||
if (winOption == WIN_OPTION.TAKE_LAST) {
|
||||
if (pileSize > maxSelection) {
|
||||
|
||||
int objectsToRemove = calculateComputersTurn(pileSizeLeft);
|
||||
|
||||
pileSize -= objectsToRemove;
|
||||
System.out.println("COMPUTER TAKES " + objectsToRemove + " AND LEAVES " + pileSize);
|
||||
gameState = GAME_STATE.PLAYERS_TURN;
|
||||
} else {
|
||||
System.out.println("COMPUTER TAKES " + pileSize + " AND WINS.");
|
||||
winner = WINNER.COMPUTER;
|
||||
gameState = GAME_STATE.ANNOUNCE_WINNER;
|
||||
}
|
||||
} else {
|
||||
pileSizeLeft--;
|
||||
if (pileSize > minSelection) {
|
||||
int objectsToRemove = calculateComputersTurn(pileSizeLeft);
|
||||
pileSize -= objectsToRemove;
|
||||
System.out.println("COMPUTER TAKES " + objectsToRemove + " AND LEAVES " + pileSize);
|
||||
gameState = GAME_STATE.PLAYERS_TURN;
|
||||
} else {
|
||||
System.out.println("COMPUTER TAKES " + pileSize + " AND LOSES.");
|
||||
winner = WINNER.PLAYER;
|
||||
gameState = GAME_STATE.ANNOUNCE_WINNER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ANNOUNCE_WINNER:
|
||||
switch (winner) {
|
||||
case PLAYER:
|
||||
System.out.println("CONGRATULATIONS, YOU WIN.");
|
||||
break;
|
||||
case COMPUTER:
|
||||
System.out.println("TOUGH LUCK, YOU LOSE.");
|
||||
break;
|
||||
}
|
||||
gameState = GAME_STATE.START_GAME;
|
||||
break;
|
||||
}
|
||||
} while (gameState != GAME_STATE.GAME_OVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out the computers turn - i.e. how many objects to remove
|
||||
*
|
||||
* @param pileSizeLeft current size
|
||||
* @return the number of objects to remove.
|
||||
*/
|
||||
private int calculateComputersTurn(int pileSizeLeft) {
|
||||
int computersNumberToRemove = pileSizeLeft - rangeOfRemovals * (pileSizeLeft / rangeOfRemovals);
|
||||
if (computersNumberToRemove < minSelection) {
|
||||
computersNumberToRemove = minSelection;
|
||||
}
|
||||
if (computersNumberToRemove > maxSelection) {
|
||||
computersNumberToRemove = maxSelection;
|
||||
}
|
||||
|
||||
return computersNumberToRemove;
|
||||
}
|
||||
|
||||
private void intro() {
|
||||
System.out.println(simulateTabs(33) + "BATNUM");
|
||||
System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
System.out.println();
|
||||
System.out.println("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE");
|
||||
System.out.println("COMPUTER IS YOUR OPPONENT.");
|
||||
System.out.println();
|
||||
System.out.println("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU");
|
||||
System.out.println("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.");
|
||||
System.out.println("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR");
|
||||
System.out.println("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.");
|
||||
System.out.println("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.");
|
||||
System.out.println("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a message on the screen, then accept input from Keyboard.
|
||||
* Converts input to Integer
|
||||
*
|
||||
* @param text message to be displayed on screen.
|
||||
* @return what was typed by the player.
|
||||
*/
|
||||
private int displayTextAndGetNumber(String text) {
|
||||
return Integer.parseInt(displayTextAndGetInput(text));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a message on the screen, then accept input from Keyboard.
|
||||
*
|
||||
* @param text message to be displayed on screen.
|
||||
* @return what was typed by the player.
|
||||
*/
|
||||
private String displayTextAndGetInput(String text) {
|
||||
System.out.print(text);
|
||||
return kbScanner.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate the old basic tab(xx) command which indented text by xx spaces.
|
||||
*
|
||||
* @param spaces number of spaces required
|
||||
* @return String with number of spaces
|
||||
*/
|
||||
private String simulateTabs(int spaces) {
|
||||
char[] spacesTemp = new char[spaces];
|
||||
Arrays.fill(spacesTemp, ' ');
|
||||
return new String(spacesTemp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a string delimited by comma's and returns the nth delimited
|
||||
* value (starting at count 0).
|
||||
*
|
||||
* @param text - text with values separated by comma's
|
||||
* @param pos - which position to return a value for
|
||||
* @return the int representation of the value
|
||||
*/
|
||||
private int getDelimitedValue(String text, int pos) {
|
||||
String[] tokens = text.split(",");
|
||||
return Integer.parseInt(tokens[pos]);
|
||||
}
|
||||
}
|
||||
8
08 Batnum/java/src/BatNumGame.java
Normal file
8
08 Batnum/java/src/BatNumGame.java
Normal file
@@ -0,0 +1,8 @@
|
||||
public class BatNumGame {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
BatNum batNum = new BatNum();
|
||||
batNum.play();
|
||||
}
|
||||
}
|
||||
9
08 Batnum/javascript/batnum.html
Normal file
9
08 Batnum/javascript/batnum.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>BATNUM</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="output" style="font-size: 12pt;"></pre>
|
||||
<script src="batnum.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
161
08 Batnum/javascript/batnum.js
Normal file
161
08 Batnum/javascript/batnum.js
Normal file
@@ -0,0 +1,161 @@
|
||||
// BATNUM
|
||||
//
|
||||
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
|
||||
//
|
||||
|
||||
function print(str)
|
||||
{
|
||||
document.getElementById("output").appendChild(document.createTextNode(str));
|
||||
}
|
||||
|
||||
function input()
|
||||
{
|
||||
var input_element;
|
||||
var input_str;
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
input_element = document.createElement("INPUT");
|
||||
|
||||
print("? ");
|
||||
input_element.setAttribute("type", "text");
|
||||
input_element.setAttribute("length", "50");
|
||||
document.getElementById("output").appendChild(input_element);
|
||||
input_element.focus();
|
||||
input_str = undefined;
|
||||
input_element.addEventListener("keydown", function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
input_str = input_element.value;
|
||||
document.getElementById("output").removeChild(input_element);
|
||||
print(input_str);
|
||||
print("\n");
|
||||
resolve(input_str);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function tab(space)
|
||||
{
|
||||
var str = "";
|
||||
while (space-- > 0)
|
||||
str += " ";
|
||||
return str;
|
||||
}
|
||||
|
||||
// Main program
|
||||
async function main()
|
||||
{
|
||||
print(tab(33) + "BATNUM\n");
|
||||
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
|
||||
print("\n");
|
||||
print("\n");
|
||||
print("\n");
|
||||
print("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE\n");
|
||||
print("COMPUTER IS YOUR OPPONENT.\n");
|
||||
print("\n");
|
||||
print("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU\n");
|
||||
print("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.\n");
|
||||
print("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR\n");
|
||||
print("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.\n");
|
||||
print("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.\n");
|
||||
print("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.\n");
|
||||
print("\n");
|
||||
first_time = 1;
|
||||
while (1) {
|
||||
while (1) {
|
||||
if (first_time == 1) {
|
||||
first_time = 0;
|
||||
} else {
|
||||
for (i = 1; i <= 10; i++)
|
||||
print("\n");
|
||||
}
|
||||
print("ENTER PILE SIZE");
|
||||
n = parseInt(await input());
|
||||
if (n >= 1)
|
||||
break;
|
||||
}
|
||||
while (1) {
|
||||
print("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ");
|
||||
m = parseInt(await input());
|
||||
if (m == 1 || m == 2)
|
||||
break;
|
||||
}
|
||||
while (1) {
|
||||
print("ENTER MIN AND MAX ");
|
||||
str = await input();
|
||||
a = parseInt(str);
|
||||
b = parseInt(str.substr(str.indexOf(",") + 1));
|
||||
if (a <= b && a >= 1)
|
||||
break;
|
||||
}
|
||||
while (1) {
|
||||
print("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ");
|
||||
s = parseInt(await input());
|
||||
print("\n");
|
||||
print("\n");
|
||||
if (s == 1 || s == 2)
|
||||
break;
|
||||
}
|
||||
w = 0;
|
||||
c = a + b;
|
||||
while (1) {
|
||||
if (s == 1) {
|
||||
// Computer's turn
|
||||
q = n;
|
||||
if (m != 1)
|
||||
q--;
|
||||
if (m != 1 && n <= a) {
|
||||
w = 1;
|
||||
print("COMPUTER TAKES " + n + " AND LOSES.\n");
|
||||
} else if (m == 1 && n <= b) {
|
||||
w = 1;
|
||||
print("COMPUTER TAKES " + n + " AND WINS.\n");
|
||||
} else {
|
||||
p = q - c * Math.floor(q / c);
|
||||
if (p < a)
|
||||
p = a;
|
||||
if (p > b)
|
||||
p = b;
|
||||
n -= p;
|
||||
print("COMPUTER TAKES " + p + " AND LEAVES " + n + "\n");
|
||||
w = 0;
|
||||
}
|
||||
s = 2;
|
||||
}
|
||||
if (w)
|
||||
break;
|
||||
if (s == 2) {
|
||||
while (1) {
|
||||
print("\n");
|
||||
print("YOUR MOVE ");
|
||||
p = parseInt(await input());
|
||||
if (p == 0) {
|
||||
print("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.\n");
|
||||
w = 1;
|
||||
break;
|
||||
} else if (p >= a && p <= b && n - p >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p != 0) {
|
||||
n -= p;
|
||||
if (n == 0) {
|
||||
if (m != 1) {
|
||||
print("TOUGH LUCK, YOU LOSE.\n");
|
||||
} else {
|
||||
print("CONGRATULATIONS, YOU WIN.\n");
|
||||
}
|
||||
w = 1;
|
||||
} else {
|
||||
w = 0;
|
||||
}
|
||||
}
|
||||
s = 1;
|
||||
}
|
||||
if (w)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user