Merge branch 'coding-horror:main' into 48_High_IQ_Python

This commit is contained in:
Thomas Kwashnak
2022-01-12 13:05:44 -05:00
committed by GitHub
104 changed files with 14096 additions and 310 deletions

19
.gitignore vendored
View File

@@ -1,3 +1,19 @@
.local/
.vscode/
.gradle/
node_modules/
buildJvm/
build.gradle
.classpath
.project
.settings
.metadata
*.iml
*.ipr
*.class
*/.vs
*.suo
@@ -11,6 +27,9 @@ obj/
out/
*.py[co]
Pipfile
.DS_Store
/.vs/basic-computer-games/v16
/.vs

View File

@@ -0,0 +1,84 @@
/**
* Program to find games that are missing solutions in a given language
*
* Scan each game folder, check for a folder for each language, and also make
* sure there's at least one file of the expected extension and not just a
* readme or something
*/
const fs = require("fs");
const glob = require("glob");
// relative path to the repository root
const ROOT_PATH = "../.";
const languages = [
{ name: "csharp", extension: "cs" },
{ name: "java", extension: "java" },
{ name: "javascript", extension: "html" },
{ name: "pascal", extension: "pas" },
{ name: "perl", extension: "pl" },
{ name: "python", extension: "py" },
{ name: "ruby", extension: "rb" },
{ name: "vbnet", extension: "vb" },
];
const getFilesRecursive = async (path, extension) => {
return new Promise((resolve, reject) => {
glob(`${path}/**/*.${extension}`, (err, matches) => {
if (err) {
reject(err);
}
resolve(matches);
});
});
};
const getPuzzleFolders = () => {
return fs
.readdirSync(ROOT_PATH, { withFileTypes: true })
.filter((dirEntry) => dirEntry.isDirectory())
.filter(
(dirEntry) =>
![".git", "node_modules", "00_Utilities"].includes(dirEntry.name)
)
.map((dirEntry) => dirEntry.name);
};
(async () => {
let missingGames = {};
let missingLanguageCounts = {};
languages.forEach((l) => (missingLanguageCounts[l.name] = 0));
const puzzles = getPuzzleFolders();
for (const puzzle of puzzles) {
for (const { name: language, extension } of languages) {
const files = await getFilesRecursive(
`${ROOT_PATH}/${puzzle}/${language}`,
extension
);
if (files.length === 0) {
if (!missingGames[puzzle]) missingGames[puzzle] = [];
missingGames[puzzle].push(language);
missingLanguageCounts[language]++;
}
}
}
const missingCount = Object.values(missingGames).flat().length;
if (missingCount === 0) {
console.log("All games have solutions for all languages");
} else {
console.log(`Missing ${missingCount} implementations:`);
Object.entries(missingGames).forEach(
([p, ls]) => (missingGames[p] = ls.join(", "))
);
console.log(`\nMissing languages by game:`);
console.table(missingGames);
console.log(`\nBy language:`);
console.table(missingLanguageCounts);
}
})();
return;

View File

@@ -0,0 +1,50 @@
import os
lang_pos = {
"csharp": 1, "java": 2, "javascript": 3,
"pascal": 4, "perl": 5, "python": 6, "ruby": 7, "vbnet": 8
}
write_string = "# TODO list \n game | csharp | java | javascript | pascal | perl | python | ruby | vbnet \n --- | --- | --- | --- | --- | --- | --- | --- | --- \n"
# Set the directory you want to start from
rootDir = '..'
strings_done = []
checklist = ["game", "csharp", "java", "javascript",
"pascal", "perl", "python", "ruby", "vbnet"]
prev_game = ""
for dirName, subdirList, fileList in os.walk(rootDir):
split_dir = dirName.split("/")
if len(split_dir) == 2 and not split_dir[1] in ['.git', '00_Utilities']:
if prev_game == "":
prev_game = split_dir[1]
checklist[0] = split_dir[1]
if prev_game != split_dir[1]:
# it's a new dir
strings_done.append(checklist)
checklist = [split_dir[1], "csharp", "java", "javascript",
"pascal", "perl", "python", "ruby", "vbnet"]
prev_game = split_dir[1]
elif len(split_dir) == 3 and split_dir[1] != '.git':
if split_dir[2] in lang_pos.keys():
if len(fileList) > 1 or len(subdirList) > 0:
# there is more files than the readme
checklist[lang_pos[split_dir[2]]] = ""
else:
checklist[lang_pos[split_dir[2]]] = "⬜️"
sorted_strings = list(map(lambda l: " | ".join(l) + "\n",
sorted(strings_done, key=lambda x: x[0])))
write_string += ''.join(sorted_strings)
with open("README.md", "w") as f:
f.write(write_string)

View File

@@ -17,3 +17,4 @@ http://www.vintage-basic.net/games.html
#### External Links
- Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp
- PowerShell: https://github.com/eweilnau/basic-computer-games-powershell/blob/main/AceyDucey.ps1

View File

@@ -1,3 +1,9 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Oracle Java](https://openjdk.java.net/)
Two versions of Acey Ducey have been contributed.
The original upload supported JDK 8/JDK 11 and uses multiple files and the second uses features in JDK 17 and is implemented in a single file AceyDucey17.java.
Both are in the src folder.

View File

@@ -0,0 +1,201 @@
import java.util.Random;
import java.util.Scanner;
/**
* A modern version (JDK17) of ACEY DUCEY using post Java 8 features. Notes
* regarding new java features or differences in the original basic
* implementation are numbered and at the bottom of this code.
* The goal is to recreate the exact look and feel of the original program
* minus a large glaring bug in the original code that lets you cheat.
*/
public class AceyDucey17 {
public static void main(String[] args) {
// notes [1]
System.out.println("""
ACEY DUCEY CARD GAME
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER
THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP
YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING
ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE
A VALUE BETWEEN THE FIRST TWO.
IF YOU DO NOT WANT TO BET, INPUT A 0""");
do {
playGame();
} while (stillInterested());
System.out.println("O.K., HOPE YOU HAD FUN!");
}
public static void playGame() {
int cashOnHand = 100; // our only mutable variable note [11]
System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");// note [6]
while (cashOnHand > 0) {
System.out.println();
System.out.println("HERE ARE YOUR NEXT TWO CARDS:");
final Card lowCard = Card.getRandomCard(2, Card.KING); //note [3]
System.out.println(lowCard);
final Card highCard = Card.getRandomCard(lowCard.rank() + 1, Card.ACE);
System.out.println(highCard);
final int bet = getBet(cashOnHand);
final int winnings = determineWinnings(lowCard,highCard,bet);
cashOnHand += winnings;
if(winnings != 0 || cashOnHand != 0){ //note [2]
System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");//note [6]
}
}
}
public static int determineWinnings(Card lowCard, Card highCard, int bet){
if (bet <= 0) { // note [5]
System.out.println("CHICKEN!!");
return 0;
}
Card nextCard = Card.getRandomCard(2, Card.ACE);
System.out.println(nextCard);
if(nextCard.between(lowCard,highCard)){
System.out.println("YOU WIN!!!");
return bet;
}
System.out.println("SORRY, YOU LOSE");
return -bet;
}
public static boolean stillInterested(){
System.out.println();
System.out.println();
System.out.println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
System.out.println();
System.out.println();
System.out.print("TRY AGAIN (YES OR NO)? ");
Scanner input = new Scanner(System.in);
boolean playAgain = input.nextLine()
.toUpperCase()
.startsWith("Y"); // note [9]
System.out.println();
System.out.println();
return playAgain;
}
public static int getBet(int cashOnHand){
int bet;
do{
System.out.println();
System.out.print("WHAT IS YOUR BET? ");
bet = inputNumber();
if (bet > cashOnHand) {
System.out.println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
System.out.println("YOU HAVE ONLY "+cashOnHand+" DOLLARS TO BET.");
}
}while(bet > cashOnHand);
return bet;
}
public static int inputNumber() {
final Scanner input = new Scanner(System.in);
// set to negative to mark as not entered yet in case of input error.
int number = -1;
while (number < 0) {
try {
number = input.nextInt();
} catch(Exception ex) { // note [7]
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
System.out.print("? ");
try{
input.nextLine();
}
catch(Exception ns_ex){ // received EOF (ctrl-d or ctrl-z if windows)
System.out.println("END OF INPUT, STOPPING PROGRAM.");
System.exit(1);
}
}
}
return number;
}
record Card(int rank){
// Some constants to describe face cards.
public static final int JACK = 11, QUEEN = 12, KING = 13, ACE = 14;
private static final Random random = new Random();
public static Card getRandomCard(int from, int to){
return new Card(random.nextInt(from, to+1)); // note [4]
}
public boolean between(Card lower, Card higher){
return lower.rank() < this.rank() && this.rank() < higher.rank();
}
@Override
public String toString() { // note [13]
return switch (rank) {
case JACK -> "JACK";
case QUEEN -> "QUEEN";
case KING -> "KING";
case ACE -> "ACE\n"; // note [10]
default -> " "+rank+" "; // note [6]
};
}
}
/*
Notes:
1. Multiline strings, a.k.a. text blocks, were added in JDK15.
2. The original game only displays the players balance if it changed,
which it does not when the player chickens out and bets zero.
It also doesn't display the balance when it becomes zero because it has
a more appropriate message: Sorry, You Lose.
3. To pick two cards to show, the original BASIC implementation has a
bug that could cause a race condition if the RND function never chose
a lower number first and higher number second. It loops infinitely
re-choosing random numbers until the condition is met of the first
one being lower. The logic is changed a bit here so that the first
card picked is anything but an ACE, the highest possible card,
and then the second card is between the just picked first card upto
and including the ACE.
4. Random.nextInt(origin, bound) was added in JDK17, and allows to
directly pick a range for a random integer to be generated. The second
parameter is exclusive of the range and thus why they are stated with
+1's to the face card.
5. The original BASIC implementation has a bug that allows negative value
bets. Since you can't bet MORE cash than you have you can always bet
less including a very, very large negative value. You would do this when
the chances of winning are slim or zero since losing a hand SUBTRACTS
your bet from your cash; subtracting a negative number actually ADDS
to your cash, potentially making you an instant billionaire.
This loophole is now closed.
6. The subtle behavior of the BASIC PRINT command causes a space to be
printed before all positive numbers as well as a trailing space. Any
place a non-face card or the players balance is printed has extra space
to mimic this behavior.
7. Errors on input were probably specific to the interpreter. This program
tries to match the Vintage Basic interpreter's error messages. The final
input.nextLine() command exists to clear the blockage of whatever
non-number input was entered. But even that could fail if the user
types Ctrl-D (windows Ctrl-Z), signifying an EOF (end of file) and thus
the closing of STDIN channel. The original program on an EOF signal prints
"END OF INPUT IN LINE 660" and thus we cover it roughly the same way.
All of this is necessary to avoid a messy stack trace from being
printed as the program crashes.
9. The original game only accepted a full upper case "YES" to continue
playing if bankrupted. This program is more lenient and will accept
any input that starts with the letter 'y', uppercase or not.
10. The original game prints an extra blank line if the card is an ACE. There
is seemingly no rationale for this.
11. Modern java best practices are edging toward a more functional paradigm
and as such, mutating state is discouraged. All other variables besides
the cashOnHand are final and initialized only once.
12. Refactoring of the concept of a card is done with a record. Records were
introduced in JDK14. Card functionality is encapsulated in this example
of a record. An enum could be a better alternative since there are
technically only 13 cards possible.
13. Switch expressions were introduced as far back as JDK12 but continue to
be refined for clarity, exhaustiveness. As of JDK17 pattern matching
for switch expressions can be accessed by enabling preview features.
*/
}

View File

@@ -90,19 +90,23 @@ async function main() {
print('\nWHAT IS YOUR BET? ');
bet = parseInt(await input(), 10);
let minimumRequiredBet = 0;
if (bet > minimumRequiredBet) {
if (bet >= minimumRequiredBet) {
if (bet > availableDollars) {
print('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
print(`YOU HAVE ONLY ${availableDollars} DOLLARS TO BET.`);
} else {
validBet = true;
}
} else {
// Does not meet minimum required bet
print('CHICKEN!!');
print('');
}
}
if (bet == 0)
{
// User chose not to bet.
print('CHICKEN!!');
print('');
// Don't draw a third card, draw a new set of 2 cards.
continue;
}
print('\n\nHERE IS THE CARD WE DREW: ');
print(getCardValue(cardThree));
@@ -127,7 +131,7 @@ async function main() {
print('');
print('');
if (isValidYesNoString(tryAgainInput)) {
if (isValidYesString(tryAgainInput)) {
availableDollars = 100;
} else {
print('O.K., HOPE YOU HAD FUN!');

View File

@@ -36,7 +36,7 @@ class Deck:
def build(self):
for suit in ['\u2665', '\u2666', '\u2663', '\u2660']:
for rank in range(1, 14):
for rank in range(2, 15):
self.cards.append(Card(suit, rank))
def shuffle(self):

295
02_Amazing/vbnet/program.vb Normal file
View File

@@ -0,0 +1,295 @@
Imports System
Module Program
Enum Directions
SolveAndReset = 0
Left = 1
Up = 2
Right = 3
Down = 4
End Enum
'Program State
Dim Width As Integer = 0, Height As Integer = 0, Q As Integer = 0, CellsVisited As Integer = 2, curCol As Integer, curRow As Integer = 1
Dim SolutionCompleted As Boolean = False
Dim CellVisitHistory(,) As Integer
Dim CellState(,) As Integer
Dim rnd As New Random()
Public ReadOnly Property BlockedLeft As Boolean
Get
Return curCol - 1 = 0 OrElse CellVisitHistory(curCol - 1, curRow) <> 0
End Get
End Property
Public ReadOnly Property BlockedAbove As Boolean
Get
Return curRow - 1 = 0 OrElse CellVisitHistory(curCol, curRow - 1) <> 0
End Get
End Property
Public ReadOnly Property BlockedRight As Boolean
Get
Return curCol = Width OrElse CellVisitHistory(curCol + 1, curRow) <> 0
End Get
End Property
'Note: "BlockedBelow" does NOT include checking if we have a solution!
Public ReadOnly Property BlockedBelow As Boolean
Get
Return curRow = Height OrElse CellVisitHistory(curCol, curRow + 1) <> 0
End Get
End Property
Public ReadOnly Property OnBottomRow As Boolean
Get
Return curRow.Equals(Height)
End Get
End Property
Sub Main(args As String())
Const header As String =
" AMAZING PROGRAM
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
"
Console.WriteLine(header)
While Width <= 1 OrElse Height <= 1
Console.Write("WHAT ARE YOUR WIDTH AND LENGTH? ")
'We no longer have the old convenient INPUT command, so need to parse out the inputs
Dim parts = Console.ReadLine().Split(","c).Select(Function(s) Convert.ToInt32(s.Trim())).ToList()
Width = parts(0)
Height = parts(1)
If Width <= 1 OrElse Height <= 1 Then Console.WriteLine($"MEANINGLESS DIMENSIONS. TRY AGAIN.{vbCrLf}")
End While
ReDim CellVisitHistory(Width, Height), CellState(Width, Height)
Console.WriteLine("
")
curCol = rnd.Next(1, Width + 1) 'Starting X position
CellVisitHistory(curCol, 1) = 1
Dim startXPos As Integer = curCol 'we need to know this at the end to print opening line
Dim keepGoing As Boolean = True
While keepGoing
If BlockedLeft Then
keepGoing = ChoosePath_BlockedToTheLeft()
ElseIf BlockedAbove Then
keepGoing = ChoosePath_BlockedAbove()
ElseIf BlockedRight Then
keepGoing = ChoosePath_BlockedToTheRight()
Else
keepGoing = SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go anywhere but down
End If
End While
PrintFinalResults(startXPos)
End Sub
Public Sub ResetCurrentPosition()
Do
If curCol <> Width Then 'not at the right edge
curCol += 1
ElseIf curRow <> Height Then 'not at the bottom
curCol = 1
curRow += 1
Else
curCol = 1
curRow = 1
End If
Loop While CellVisitHistory(curCol, curRow) = 0
End Sub
Dim methods() As Func(Of Boolean) = {AddressOf MarkSolvedAndResetPosition, AddressOf GoLeft, AddressOf GoUp, AddressOf GoRight, AddressOf GoDown}
Public Function SelectRandomDirection(ParamArray possibles() As Directions) As Boolean
Dim x As Integer = rnd.Next(0, possibles.Length)
Return methods(possibles(x))()
End Function
Public Function ChoosePath_BlockedToTheLeft() As Boolean
If BlockedAbove Then
If BlockedRight Then
If curRow <> Height Then
If CellVisitHistory(curCol, curRow + 1) <> 0 Then ' Can't go down, but not at the edge...blocked. Reset and try again
ResetCurrentPosition()
Return True
Else
Return GoDown()
End If
ElseIf SolutionCompleted Then 'Can't go Down (there's already another solution)
ResetCurrentPosition()
Return True
Else 'Can't go LEFT, UP, RIGHT, or DOWN, but we're on the bottom and there's no solution yet
Return MarkSolvedAndResetPosition()
End If
ElseIf BlockedBelow Then
Return GoRight()
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Right, Directions.Down)
ElseIf SolutionCompleted Then 'Can only go right, and we're at the bottom
Return GoRight()
Else 'Can only go right, we're at the bottom, and there's not a solution yet
Return SelectRandomDirection(Directions.Right, Directions.SolveAndReset)
End If
'== Definitely can go Up ==
ElseIf BlockedRight Then
If BlockedBelow Then
Return GoUp()
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Up, Directions.Down)
ElseIf SolutionCompleted Then 'We're on the bottom row, can only go up
Return GoUp()
Else 'We're on the bottom row, can only go up, but there's no solution
Return SelectRandomDirection(Directions.Up, Directions.SolveAndReset)
End If
'== Definitely can go Up and Right ==
ElseIf BlockedBelow Then
Return SelectRandomDirection(Directions.Up, Directions.Right)
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.Down)
ElseIf SolutionCompleted Then 'at the bottom, but already have a solution
Return SelectRandomDirection(Directions.Up, Directions.Right)
Else
Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.SolveAndReset)
End If
End Function
Public Function ChoosePath_BlockedAbove() As Boolean
'No need to check the left side, only called from the "keepGoing" loop where LEFT is already cleared
If BlockedRight Then
If BlockedBelow Then
Return GoLeft()
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Left, Directions.Down)
ElseIf SolutionCompleted Then 'Can't go down because there's already a solution
Return GoLeft()
Else 'At the bottom, no solution yet...
Return SelectRandomDirection(Directions.Left, Directions.SolveAndReset)
End If
ElseIf BlockedBelow Then
Return SelectRandomDirection(Directions.Left, Directions.Right)
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.Down)
ElseIf SolutionCompleted Then
Return SelectRandomDirection(Directions.Left, Directions.Right)
Else
Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.SolveAndReset)
End If
End Function
Public Function ChoosePath_BlockedToTheRight() As Boolean
'No need to check Left or Up, only called from the "keepGoing" loop where LEFT and UP are already cleared
If BlockedRight Then 'Can't go Right -- why? we knew this when calling the function
If BlockedBelow Then
Return SelectRandomDirection(Directions.Left, Directions.Up)
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Down)
ElseIf SolutionCompleted Then
Return SelectRandomDirection(Directions.Left, Directions.Up)
Else
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.SolveAndReset)
End If
Else 'Should never get here
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go Left, Up, or Right (but path is blocked?)
End If
End Function
Public Sub PrintFinalResults(startPos As Integer)
For i As Integer = 0 To Width - 1
If i = startPos Then Console.Write(". ") Else Console.Write(".--")
Next
Console.WriteLine(".")
If Not SolutionCompleted Then 'Pick a random exit
Dim X As Integer = rnd.Next(1, Width + 1)
If CellState(X, Height) = 0 Then
CellState(X, Height) = 1
Else
CellState(X, Height) = 3
End If
End If
For j As Integer = 1 To Height
Console.Write("I")
For i As Integer = 1 To Width
If CellState(i, j) < 2 Then
Console.Write(" I")
Else
Console.Write(" ")
End If
Next
Console.WriteLine()
For i As Integer = 1 To Width
If CellState(i, j) = 0 OrElse CellState(i, j) = 2 Then
Console.Write(":--")
Else
Console.Write(": ")
End If
Next
Console.WriteLine(".")
Next
End Sub
Public Function GoLeft() As Boolean
curCol -= 1
CellVisitHistory(curCol, curRow) = CellsVisited
CellsVisited += 1
CellState(curCol, curRow) = 2
If CellsVisited > Width * Height Then Return False
Q = 0
Return True
End Function
Public Function GoUp() As Boolean
curRow -= 1
CellVisitHistory(curCol, curRow) = CellsVisited
CellsVisited += 1
CellState(curCol, curRow) = 1
If CellsVisited > Width * Height Then Return False
Q = 0
Return True
End Function
Public Function GoRight() As Boolean
CellVisitHistory(curCol + 1, curRow) = CellsVisited
CellsVisited += 1
If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 2 Else CellState(curCol, curRow) = 3
curCol += 1
If CellsVisited > Width * Height Then Return False
Return ChoosePath_BlockedToTheLeft()
End Function
Public Function GoDown() As Boolean
If Q = 1 Then Return MarkSolvedAndResetPosition()
CellVisitHistory(curCol, curRow + 1) = CellsVisited
CellsVisited += 1
If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 1 Else CellState(curCol, curRow) = 3
curRow += 1
If CellsVisited > Width * Height Then Return False
Return True
End Function
Public Function MarkSolvedAndResetPosition() As Boolean
' AlWAYS returns true
SolutionCompleted = True
Q = 1
If CellState(curCol, curRow) = 0 Then
CellState(curCol, curRow) = 1
curCol = 1
curRow = 1
If CellVisitHistory(curCol, curRow) = 0 Then ResetCurrentPosition()
Else
CellState(curCol, curRow) = 3
ResetCurrentPosition()
End If
Return True
End Function
End Module

View File

@@ -37,14 +37,14 @@ fun main() {
// an answer or a blank string
fun ask(question: String): String {
print("$question? ")
return readLine()?.uppercase() ?: ""
return readln().uppercase() ?: ""
}
// Special case for a "yes or no" question, returns true of yes
fun askYesOrNo(question: String): Boolean {
return generateSequence {
print("$question? ")
readLine()
readln()
}.firstNotNullOf { yesOrNo(it) }
}

268
04_Awari/perl/awari.pl Normal file
View File

@@ -0,0 +1,268 @@
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use List::Util 'none';
# our board will be represented with an array of 14 slots, from 0 to 13.
# Positions 6 and 13 represent the "home pit" for the human and the
# computer, respectively.
use constant PLAYER_HOME => 6;
use constant COMPUTER_HOME => 13;
use constant FIRST => 0;
use constant AGAIN => 1;
exit main(@ARGV);
sub main {
$|++; # disable buffering on standard output, every print will be
# done immediately
welcome(); # startup message
# this array will keep track of computer-side failures, defined as
# "the computer did not win". Whenever the computer loses or draws, the
# specific sequence of moves will be saved and then used to drive
# the search for a (hopefully) optimal move.
my $failures = [];
while ('enjoying') {
# a new game starts, let's reset the board to the initial condition
my $board = [ (3) x 6, 0, (3) x 6, 0 ];
# this string will keep track of all moves performed
my $moves = '/';
# the human player starts
my $turn = 'player';
say "\n";
print_board($board);
while (not is_game_over($board)) {
my $move; # this will collect the move in this turn
if ($turn eq 'player') { # "first" move for player
# player_move(...) does the move selected by the player,
# returning both the selected move as well as the pit id
# where the last seed landed
($move, my $landing) = player_move($board);
# if we landed on the Player's Home Pit we get another move
$turn = $landing == PLAYER_HOME ? 'player-again' : 'computer';
}
elsif ($turn eq 'player-again') { # "second" move for player
# here we call player_move making it clear that it's the
# second move, to get the right prompt eventually. We only
# care for the $move as the result, so we ignore the other.
($move) = player_move($board, AGAIN);
$turn = 'computer';
}
else {
# the computer_move(...) function analyzes the $board as well
# as adapting the strategy based on past "failures" (i.e.
# matches where the computer did not win). For this it's
# important to pass the log of these failures, as well as the
# full record of moves in this specific match.
($move, my $landing) = computer_move($board, $failures, $moves);
print "\nMY MOVE IS ", $move - 6;
# do the second move in the turn if conditions apply
if ($landing == COMPUTER_HOME && ! is_game_over($board)) {
# save the first move before doing the second one!
$moves .= "$move/";
my ($move) = computer_move($board, $failures, $moves);
print ',', $move - 6;
}
$turn = 'player';
}
# append the last selected move by either party, to track this
# specific match (useful for computer's AI and ML)
$moves .= "$move/";
print_board($board);
}
# assess_victory() returns the difference between player's and
# computer's seeds, so a negative value is a win for the computer.
my $computer_won = assess_victory($board) < 0;
# if this last match was a "failure" (read: not a win for the
# computer), then record it for future memory.
push $failures->@*, $moves unless $computer_won;
}
return 0;
}
# calculate the difference between the two home pits. Negative values mean
# that the computer won, 0 is a draw, positive values is a player's win.
# The difference is also returned back, in case of need.
sub assess_victory ($board) {
say "\nGAME OVER";
my $difference = $board->[PLAYER_HOME] - $board->[COMPUTER_HOME];
if ($difference < 0) {
say 'I WIN BY ', -$difference, ' POINTS';
}
else {
say $difference ? "YOU WIN BY $difference POINTS" : 'DRAWN GAME';
}
return $difference;
}
# move the seeds from $pit and take into account possible bonuses
sub move_seeds ($board, $pit) {
# get the seeds from the selected pit $pit
my $seeds = $board->[$pit];
$board->[$pit] = 0;
# $landing will be our "moving cursor" to place seeds around
my $landing = $pit;
while ($seeds > 0) {
$landing = ($landing + 1) % 14; # 12 --> 13 -[wrap]-> 0 --> 1
--$seeds;
++$board->[$landing];
}
# check for "stealing seeds" condition. This cannot happen in home pits
if ($landing != PLAYER_HOME && $landing != COMPUTER_HOME
&& $board->[$landing] == 1 && $board->[12 - $landing] > 0) {
my $home = $pit < 7 ? PLAYER_HOME : COMPUTER_HOME;
$board->[$home] += 1 + $board->[12 - $landing];
$board->@[$landing, 12 - $landing] = (0, 0);
}
return ($pit, $landing);
}
sub get_player_move ($board, $prompt) {
print "\n$prompt? ";
while (defined(my $move = <STDIN>)) {
chomp($move); # remove newline
return $move - 1 if $move =~ m{\A[1-6]\z}mxs && $board->[$move - 1];
print 'ILLEGAL MOVE\nAGAIN? ';
}
die "goodbye\n";
}
sub player_move ($board, $stage = FIRST) {
my $prompt = $stage == FIRST ? 'YOUR MOVE' : 'AGAIN';
my $selected_move = get_player_move($board, $prompt);
return move_seeds($board, $selected_move);
}
sub computer_move ($board, $failures, $moves) {
# we will go through all possible moves for the computer and all
# possible responses by the player, collecting the "best" move in terms
# of reasonable outcome (assuming that each side wants to maximize their
# outcome. $best_move will eventually contain the best move for the
# computer, and $best_difference the best difference in scoring (as
# seen from the computer).
my ($best_move, $best_difference);
for my $c_move (7 .. 12) {
next unless $board->[$c_move]; # only consider pits with seeds inside
# we work on a copy of the board to do all our trial-and-errors
my $copy = [ $board->@* ];
move_seeds($copy, $c_move);
# it's time to "think like a player" and see what's the "best" move
# for the player in this situation. This heuristic is "not perfect"
# but it seems OK anyway.
my $best_player_score = 0;
for my $p_move (0 .. 5) {
next unless $copy->[$p_move]; # only pits with seeds inside
my $landing = $copy->[$p_move] + $p_move;
# the player's score for this move, calculated as additional seeds
# placed in the player's pit. The original algorithm sets this to
# 1 only if the $landing position is greater than 13, which can
# be obtained by setting the ORIGINAL environment variable to a
# "true" value (in Perl terms). Otherwise it is calculated
# according to the real rules for the game.
my $p_score = $ENV{ORIGINAL} ? $landing > 13
: ($landing + 1) % 14 > 6;
# whatever, the landing position must be within the bounds
$landing %= 14;
# if the conditions apply, the player's move might win additional
# seeds, which we have to to take into account.
$p_score += $copy->[12 - $landing]
if $copy->[$landing] == 0
&& $landing != PLAYER_HOME && $landing != COMPUTER_HOME;
# let's compare this move's score against the best collected
# so far (as a response to a specific computer's move).
$best_player_score = $p_score if $p_score > $best_player_score;
}
# the overall score for the player is the additional seeds we just
# calculated into $best_player_score plus the seeds that were already
# in the player's pit
$best_player_score += $copy->[PLAYER_HOME];
# the best difference we can aim for with this computer's move must
# assume that the player will try its best
my $difference = $copy->[COMPUTER_HOME] - $best_player_score;
# now it's time to check this computer's move against the history
# of failed matches. $candidate_moves will be the "candidate" list
# of moves if we accept this one.
my $candidate_moves = $moves . $c_move . '/';
for my $failure ($failures->@*) {
# index(.) returns 0 if and only if $candidate_moves appears at
# the very beginning of $failure, i.e. it matches a previous
# behaviour.
next if index($failure, $candidate_moves) != 0;
# same sequence of moves as before... assign a penalty
$difference -= 2;
}
# update $best_move and $best_difference if they need to
($best_move, $best_difference) = ($c_move, $difference)
if (! defined $best_move) || ($best_difference < $difference);
}
# apply the selected move and return
return move_seeds($board, $best_move);
}
sub welcome {
say ' ' x 34, 'AWARI';
say ' ' x 15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY';
}
sub print_board ($board) {
my $template = '
%2d %2d %2d %2d %2d %2d
%2d %d
%2d %2d %2d %2d %2d %2d
';
printf $template, $board->@[12, 11, 10, 9, 8 , 7, 13, 6, 0 .. 5];
return;
}
sub is_game_over ($board) {
# game over if the player's side is empty
return 1 if none { $_ } $board->@[0 .. 5];
# game over if the computers' side is empty
return 1 if none { $_ } $board->@[7 .. 12];
# not game over
return 0;
}

View File

@@ -1,3 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Ruby](https://www.ruby-lang.org/en/)
Conversion to [Ruby](https://www.ruby-lang.org/en/) by [Alex Scown](https://github.com/TheScown)

311
04_Awari/ruby/awari.rb Normal file
View File

@@ -0,0 +1,311 @@
require 'strscan'
# Prints a number according to Vintage Basic's PRINT statement
# @param n The number to print
def print_number(n)
# PRINT adds padding after a number and before a positive number
print ' ' if n >= 0
print n.to_s
print ' '
end
# Mimic the INPUT statement using Vintage Basic as a reference
# @param prompt The prompt to show to the user
# @return An array of strings representing the inputted values
def input(prompt)
prompt_suffix = '? '
print "#{prompt}#{prompt_suffix}"
input = gets.chomp.strip
scanner = StringScanner.new(input)
input_values = []
until scanner.eos?
scanner.scan(/\s+/)
if scanner.check(/"/)
scanner.scan(/"/)
next_string = scanner.scan_until(/"/)
if next_string
# Remove the trailing close quote
next_string.chomp!('"')
else
# No close quote Vintage Basic crashes in this case
raise 'Unmatched quotes in input'
end
elsif scanner.exist?(/,/)
next_string = scanner.scan_until(/,/).chomp(',')
else
next_string = scanner.scan_until(/\s+|$/).rstrip
end
input_values << next_string
end
input_values << '' if input_values.empty?
input_values
end
class Game
def initialize(history, non_win_count)
@beans = Array.new(13, 3)
@beans[6] = 0
@beans[13] = 0
@turn_counter = 0
@history = history
@non_win_count = non_win_count
end
# @return [Boolean] True if the computer did not win the game
def play
while true
print_beans
move = get_move("YOUR MOVE")
home_pit = 6
computer_home_pit = 13
last_pit = perform_move(move, home_pit)
print_beans
break if game_over
if home_pit == last_pit
second_move = get_move("AGAIN")
perform_move(second_move, home_pit)
print_beans
break if game_over
end
computer_move, computer_last_pit = get_computer_move
print "MY MOVE IS #{computer_move - 6}"
break if game_over
if computer_last_pit == computer_home_pit
second_computer_move, _ = get_computer_move
print ",#{second_computer_move - 6}"
break if game_over
end
end
end_game
end
private
def game_over
@beans[0...6].all? { |b| b == 0 } || @beans[7...13].all? { |b| b == 0 }
end
# @return [Boolean] True if the computer did not win
def end_game
puts
puts "GAME OVER"
difference = @beans[6] - @beans[13]
if difference < 0
puts "I WIN BY #{-difference} POINTS"
return
end
puts "YOU WIN BY #{difference} POINTS" if difference > 0
puts "DRAWN GAME" if difference == 0
difference >= 0
end
# @param [Integer] move
# @param [Integer] home_pit
def perform_move(move, home_pit)
last_pit = distribute_beans(move, home_pit)
update_history(move)
last_pit
end
def update_history(current_move)
k = current_move % 7
@turn_counter += 1
# Add the move to the history
@history[@non_win_count] = @history[@non_win_count] * 6 + k if @turn_counter < 9
end
def print_beans
puts
# Print computer beans
print ' ' * 3
@beans[7...13].reverse.each { |bean_count| print_bean(bean_count) }
puts
# Print home beans
print_bean(@beans[13])
print ' ' * 23
print_number(@beans[6]) # This is not print_bean in line with the original version
puts
# Print player beans
print ' ' * 3
@beans[0...6].each { |bean_count| print_bean(bean_count) }
puts
puts
end
def get_move(prompt)
move = get_integer_input(prompt)
while move < 1 || move > 6 || @beans[move - 1] == 0
puts "ILLEGAL MOVE"
move = get_integer_input("AGAIN")
end
move - 1
end
def distribute_beans(start_pit, home_pit, beans = @beans)
beans_to_distribute = beans[start_pit]
beans[start_pit] = 0
current_pit = start_pit
(0...beans_to_distribute).each do
current_pit = (current_pit + 1) % beans.size
beans[current_pit] += 1
end
# If the last pit was empty before we put a bean in it (and it's not a scoring pit), add beans to score
if beans[current_pit] == 1 && current_pit != 6 && current_pit != 13 && beans[12 - current_pit] != 0
beans[home_pit] = beans[home_pit] + beans[12 - current_pit] + 1
beans[current_pit] = 0
beans[12 - current_pit] = 0
end
current_pit
end
def print_bean(bean_count)
print ' ' if bean_count < 10
print_number(bean_count)
end
def get_integer_input(prompt)
integer_value = nil
input_values = input(prompt)
while integer_value.nil?
print '!EXTRA INPUT IGNORED' if (input_values.size > 1)
value = input_values.first
begin
integer_value = Integer(value)
rescue
puts '!NUMBER EXPECTED - RETRY INPUT LINE'
input_values = input('')
end
end
integer_value
end
def get_computer_move
d = -99
home_pit = 13
chosen_move = 7
# Test all possible moves
(7...13).each do |move_under_test|
# Create a copy of the beans to test against
beans_copy = @beans.dup
# If the move is not legal, skip it
next if beans_copy[move_under_test] == 0
# Determine the best response the player may make to this move
player_max_score = 0
# Make the move under test against the copy
distribute_beans(move_under_test, home_pit, beans_copy)
# Test every player response
(0...6).each do |i|
# Skip the move if it would be illegal
next if beans_copy[i] == 0
# Determine the last
landing_with_overflow = beans_copy[i] + i
# If landing > 13 the player has put a bean in both home pits
player_move_score = (landing_with_overflow > 14) ? 1 : 0
# Find the actual pit
landing = landing_with_overflow % 14
# If the landing pit is empty, the player will steal beans
if beans_copy[landing] == 0 && landing != 6 && landing != 13
player_move_score = beans_copy[12 - landing] + player_move_score
end
# Update the max score if this move is the best player move
player_max_score = player_move_score if player_move_score > player_max_score
end
# Final score for move is computer score, minus the player's score and any player gains from their best move
final_score = beans_copy[13] - beans_copy[6] - player_max_score
if @turn_counter < 9
k = move_under_test % 7
(0...@non_win_count).each do |i|
# Penalise move if it was used in a losing game
final_score = final_score - 2 if @history[@non_win_count] * 6 + k == ((Float(@history[i]) / 6 ** (7 - @turn_counter)) + 0.1).floor
end
end
# Choose the move if it is the best move found so far
if final_score >= d
chosen_move = move_under_test
d = final_score
end
end
last_pit = perform_move(chosen_move, home_pit)
[chosen_move, last_pit]
end
end
puts 'AWARI'.center(80)
puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(80)
# Initialise stable variables
history = Array.new(50)
non_win_count = 0
# APPLICATION LOOP
while true
puts
puts
history[non_win_count] = 0
game = Game.new(history, non_win_count)
computer_didnt_win = game.play
non_win_count += 1 if computer_didnt_win
end

View File

@@ -0,0 +1,343 @@
import random
# The basketball class is a computer game that allows you to play as
# Dartmouth College's captain and playmaker
# The game uses set probabilites to simulate outcomes of each posession
# You are able to choose your shot types as well as defensive formations
class Basketball():
def __init__(self):
self.time = 0
self.score = [0, 0] # first value is opponents score, second is home
self.defense = None
self.defense_choices = [6, 6.5, 7, 7.5]
self.shot = None
self.shot_choices = [1, 2, 3, 4]
self.z1 = None
# Explains the keyboard inputs
print("\t\t\t Basketball")
print("\t Creative Computing Morristown, New Jersey\n\n\n")
print("This is Dartmouth College basketball. ")
print("Υou will be Dartmouth captain and playmaker.")
print("Call shots as follows:")
print("1. Long (30ft.) Jump Shot; 2. Short (15 ft.) Jump Shot; "
+ "3. Lay up; 4. Set Shot")
print("Both teams will use the same defense. Call Defense as follows:")
print("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.")
print("To change defense, just type 0 as your next shot.")
print("Your starting defense will be? ", end='')
# takes input for a defense
try:
self.defense = float(input())
except ValueError:
self.defense = None
# if the input wasn't a valid defense, takes input again
while self.defense not in self.defense_choices:
print("Your new defensive allignment is? ", end='')
try:
self.defense = float(input())
except ValueError:
continue
# takes input for opponent's name
print("\nChoose your opponent? ", end='')
self.opponent = input()
self.start_of_period()
# adds points to the score
# team can take 0 or 1, for opponent or Dartmouth, respectively
def add_points(self, team, points):
self.score[team] += points
self.print_score()
def ball_passed_back(self):
print("Ball passed back to you. ", end='')
self.dartmouth_ball()
# change defense, called when the user enters 0 for their shot
def change_defense(self):
self.defense = None
while self.defense not in self.defense_choices:
print("Your new defensive allignment is? ")
try:
self.defense = float(input())
except ValueError:
continue
self.dartmouth_ball()
# simulates two foul shots for a player and adds the points
def foul_shots(self, team):
print("Shooter fouled. Two shots.")
if random.random() > .49:
if random.random() > .75:
print("Both shots missed.")
else:
print("Shooter makes one shot and misses one.")
self.score[team] += 1
else:
print("Shooter makes both shots.")
self.score[team] += 2
self.print_score()
# called when t = 50, starts a new period
def halftime(self):
print("\n ***** End of first half *****\n")
self.print_score()
self.start_of_period()
# prints the current score
def print_score(self):
print("Score: " + str(self.score[1])
+ " to " + str(self.score[0]) + "\n")
# simulates a center jump for posession at the beginning of a period
def start_of_period(self):
print("Center jump")
if random.random() > .6:
print("Dartmouth controls the tap.\n")
self.dartmouth_ball()
else:
print(self.opponent + " controls the tap.\n")
self.opponent_ball()
# called when t = 92
def two_minute_warning(self):
print(" *** Two minutes left in the game ***")
# called when the user enters 1 or 2 for their shot
def dartmouth_jump_shot(self):
self.time += 1
if self.time == 50:
self.halftime()
elif self.time == 92:
self.two_minute_warning()
print("Jump Shot.")
# simulates chances of different possible outcomes
if random.random() > .341 * self.defense / 8:
if random.random() > .682 * self.defense / 8:
if random.random() > .782 * self.defense / 8:
if random.random() > .843 * self.defense / 8:
print("Charging foul. Dartmouth loses ball.\n")
self.opponent_ball()
else:
# player is fouled
self.foul_shots(1)
self.opponent_ball()
else:
if random.random() > .5:
print("Shot is blocked. Ball controlled by " +
self.opponent + ".\n")
self.opponent_ball()
else:
print("Shot is blocked. Ball controlled by Dartmouth.")
self.dartmouth_ball()
else:
print("Shot is off target.")
if self.defense / 6 * random.random() > .45:
print("Rebound to " + self.opponent + "\n")
self.opponent_ball()
else:
print("Dartmouth controls the rebound.")
if random.random() > .4:
if self.defense == 6 and random.random() > .6:
print("Pass stolen by " + self.opponent
+ ", easy lay up")
self.add_points(0, 2)
self.dartmouth_ball()
else:
# ball is passed back to you
self.ball_passed_back()
else:
print("")
self.dartmouth_non_jump_shot()
else:
print("Shot is good.")
self.add_points(1, 2)
self.opponent_ball()
# called when the user enters 0, 3, or 4
# lay up, set shot, or defense change
def dartmouth_non_jump_shot(self):
self.time += 1
if self.time == 50:
self.halftime()
elif self.time == 92:
self.two_minute_warning()
if self.shot == 4:
print("Set shot.")
elif self.shot == 3:
print("Lay up.")
elif self.shot == 0:
self.change_defense()
# simulates different outcomes after a lay up or set shot
if 7/self.defense*random.random() > .4:
if 7/self.defense*random.random() > .7:
if 7/self.defense*random.random() > .875:
if 7/self.defense*random.random() > .925:
print("Charging foul. Dartmouth loses the ball.\n")
self.opponent_ball()
else:
print("Shot blocked. " + self.opponent + "'s ball.\n")
self.opponent_ball()
else:
self.foul_shots(1)
self.opponent_ball()
else:
print("Shot is off the rim.")
if random.random() > 2/3:
print("Dartmouth controls the rebound.")
if random.random() > .4:
print("Ball passed back to you.\n")
self.dartmouth_ball()
else:
self.dartmouth_non_jump_shot()
else:
print(self.opponent + " controls the rebound.\n")
self.opponent_ball()
else:
print("Shot is good. Two points.")
self.add_points(1, 2)
self.opponent_ball()
# plays out a Dartmouth posession, starting with your choice of shot
def dartmouth_ball(self):
print("Your shot? ", end='')
self.shot = None
try:
self.shot = int(input())
except ValueError:
self.shot = None
while self.shot not in self.shot_choices:
print("Incorrect answer. Retype it. Your shot? ", end='')
try:
self.shot = int(input())
except:
continue
if self.time < 100 or random.random() < .5:
if self.shot == 1 or self.shot == 2:
self.dartmouth_jump_shot()
else:
self.dartmouth_non_jump_shot()
else:
if self.score[0] != self.score[1]:
print("\n ***** End Of Game *****")
print("Final Score: Dartmouth: " + str(self.score[1]) + " "
+ self.opponent + ": " + str(self.score[0]))
else:
print("\n ***** End Of Second Half *****")
print("Score at end of regulation time:")
print(" Dartmouth: " + str(self.score[1]) + " " +
self.opponent + ": " + str(self.score[0]))
print("Begin two minute overtime period")
self.time = 93
self.start_of_period()
# simulates the opponents jumpshot
def opponent_jumpshot(self):
print("Jump Shot.")
if 8/self.defense*random.random() > .35:
if 8/self.defense*random.random() > .75:
if 8/self.defense*random.random() > .9:
print("Offensive foul. Dartmouth's ball.\n")
self.dartmouth_ball()
else:
self.foul_shots(0)
self.dartmouth_ball()
else:
print("Shot is off the rim.")
if self.defense/6*random.random() > .5:
print(self.opponent + " controls the rebound.")
if self.defense == 6:
if random.random() > .75:
print("Ball stolen. Easy lay up for Dartmouth.")
self.add_points(1, 2)
self.opponent_ball()
else:
if random.random() > .5:
print("")
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent +
" guard.\n")
self.opponent_ball()
else:
if random.random() > .5:
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent +
" guard.\n")
self.opponent_ball()
else:
print("Dartmouth controls the rebound.\n")
self.dartmouth_ball()
else:
print("Shot is good.")
self.add_points(0, 2)
self.dartmouth_ball()
# simulates opponents lay up or set shot
def opponent_non_jumpshot(self):
if self.z1 > 3:
print("Set shot.")
else:
print("Lay up")
if 7/self.defense*random.random() > .413:
print("Shot is missed.")
if self.defense/6*random.random() > .5:
print(self.opponent + " controls the rebound.")
if self.defense == 6:
if random.random() > .75:
print("Ball stolen. Easy lay up for Dartmouth.")
self.add_points(1, 2)
self.opponent_ball()
else:
if random.random() > .5:
print("")
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent +
" guard.\n")
self.opponent_ball()
else:
if random.random() > .5:
print("")
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent + " guard\n")
self.opponent_ball()
else:
print("Dartmouth controls the rebound.\n")
self.dartmouth_ball()
else:
print("Shot is good.")
self.add_points(0, 2)
self.dartmouth_ball()
# simulates an opponents possesion
# #randomly picks jump shot or lay up / set shot.
def opponent_ball(self):
self.time += 1
if self.time == 50:
self.halftime()
self.z1 = 10/4*random.random()+1
if self.z1 > 2:
self.opponent_non_jumpshot()
else:
self.opponent_jumpshot()
new_game = Basketball()

1
09_Battle/java/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*~

168
09_Battle/java/Battle.java Normal file
View File

@@ -0,0 +1,168 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.function.Predicate;
import java.text.NumberFormat;
/* This class holds the game state and the game logic */
public class Battle {
/* parameters of the game */
private int seaSize;
private int[] sizes;
private int[] counts;
/* The game setup - the ships and the sea */
private ArrayList<Ship> ships;
private Sea sea;
/* game state counts */
private int[] losses; // how many of each type of ship have been sunk
private int hits; // how many hits the player has made
private int misses; // how many misses the player has made
// Names of ships of each size. The game as written has ships of size 3, 4 and 5 but
// can easily be modified. It makes no sense to have a ship of size zero though.
private static String NAMES_BY_SIZE[] = {
"error",
"size1",
"destroyer",
"cruiser",
"aircraft carrier",
"size5" };
// Entrypoint
public static void main(String args[]) {
Battle game = new Battle(6, // Sea is 6 x 6 tiles
new int[] { 2, 3, 4 }, // Ships are of sizes 2, 3, and 4
new int[] { 2, 2, 2 }); // there are two ships of each size
game.play();
}
public Battle(int scale, int[] shipSizes, int[] shipCounts) {
seaSize = scale;
sizes = shipSizes;
counts = shipCounts;
// validate parameters
if (seaSize < 4) throw new RuntimeException("Sea Size " + seaSize + " invalid, must be at least 4");
for (int sz : sizes) {
if ((sz < 1) || (sz > seaSize))
throw new RuntimeException("Ship has invalid size " + sz);
}
if (counts.length != sizes.length) {
throw new RuntimeException("Ship counts must match");
}
// Initialize game state
sea = new Sea(seaSize); // holds what ship if any occupies each tile
ships = new ArrayList<Ship>(); // positions and states of all the ships
losses = new int[counts.length]; // how many ships of each type have been sunk
// Build up the list of all the ships
int shipNumber = 1;
for (int type = 0; type < counts.length; ++type) {
for (int i = 0; i < counts[i]; ++i) {
ships.add(new Ship(shipNumber++, sizes[type]));
}
}
// When we put the ships in the sea, we put the biggest ones in first, or they might
// not fit
ArrayList<Ship> largestFirst = new ArrayList<>(ships);
Collections.sort(largestFirst, Comparator.comparingInt((Ship ship) -> ship.size()).reversed());
// place each ship into the sea
for (Ship ship : largestFirst) {
ship.placeRandom(sea);
}
}
public void play() {
System.out.println("The following code of the bad guys' fleet disposition\nhas been captured but not decoded:\n");
System.out.println(sea.encodedDump());
System.out.println("De-code it and use it if you can\nbut keep the de-coding method a secret.\n");
int lost = 0;
System.out.println("Start game");
Input input = new Input(seaSize);
try {
while (lost < ships.size()) { // the game continues while some ships remain unsunk
if (! input.readCoordinates()) { // ... unless there is no more input from the user
return;
}
// The computer thinks of the sea as a grid of rows, from top to bottom.
// However, the user will use X and Y coordinates, with Y going bottom to top
int row = seaSize - input.y();
int col = input.x() - 1;
if (sea.isEmpty(col, row)) {
++misses;
System.out.println("Splash! Try again.");
} else {
Ship ship = ships.get(sea.get(col, row) - 1);
if (ship.isSunk()) {
++misses;
System.out.println("There used to be a ship at that point, but you sunk it.");
System.out.println("Splash! Try again.");
} else if (ship.wasHit(col, row)) {
++misses;
System.out.println("You already put a hole in ship number " + ship.id());
System.out.println("Splash! Try again.");
} else {
ship.hit(col, row);
++hits;
System.out.println("A direct hit on ship number " + ship.id());
// If a ship was hit, we need to know whether it was sunk.
// If so, tell the player and update our counts
if (ship.isSunk()) {
++lost;
System.out.println("And you sunk it. Hurrah for the good guys.");
System.out.print("So far, the bad guys have lost ");
ArrayList<String> typeDescription = new ArrayList<>();
for (int i = 0 ; i < sizes.length; ++i) {
if (sizes[i] == ship.size()) {
++losses[i];
}
StringBuilder sb = new StringBuilder();
sb.append(losses[i]);
sb.append(" ");
sb.append(NAMES_BY_SIZE[sizes[i]]);
if (losses[i] != 1)
sb.append("s");
typeDescription.add(sb.toString());
}
System.out.println(String.join(", ", typeDescription));
double ratioNum = ((double)misses)/hits;
String ratio = NumberFormat.getInstance().format(ratioNum);
System.out.println("Your current splash/hit ratio is " + ratio);
if (lost == ships.size()) {
System.out.println("You have totally wiped out the bad guys' fleet");
System.out.println("With a final splash/hit ratio of " + ratio);
if (misses == 0) {
System.out.println("Congratulations - A direct hit every time.");
}
System.out.println("\n****************************\n");
}
}
}
}
}
}
catch (IOException e) {
// This should not happen running from console, but java requires us to check for it
System.err.println("System error.\n" + e);
}
}
}

67
09_Battle/java/Input.java Normal file
View File

@@ -0,0 +1,67 @@
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.text.NumberFormat;
// This class handles reading input from the player
// Each input is an x and y coordinate
// e.g. 5,3
public class Input {
private BufferedReader reader;
private NumberFormat parser;
private int scale; // size of the sea, needed to validate input
private boolean isQuit; // whether the input has ended
private int[] coords; // the last coordinates read
public Input(int seaSize) {
scale = seaSize;
reader = new BufferedReader(new InputStreamReader(System.in));
parser = NumberFormat.getIntegerInstance();
}
public boolean readCoordinates() throws IOException {
while (true) {
// Write a prompt
System.out.print("\nTarget x,y\n> ");
String inputLine = reader.readLine();
if (inputLine == null) {
// If the input stream is ended, there is no way to continue the game
System.out.println("\nGame quit\n");
isQuit = true;
return false;
}
// split the input into two fields
String[] fields = inputLine.split(",");
if (fields.length != 2) {
// has to be exactly two
System.out.println("Need two coordinates separated by ','");
continue;
}
coords = new int[2];
boolean error = false;
// each field should contain an integer from 1 to the size of the sea
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) {
// this happens if the field is not a valid number
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]; }
}

60
09_Battle/java/Sea.java Normal file
View File

@@ -0,0 +1,60 @@
// Track the content of the sea
class Sea {
// the sea is a square grid of tiles. It is a one-dimensional array, and this
// class maps x and y coordinates to an array index
// Each tile is either empty (value of tiles at index is 0)
// or contains a ship (value of tiles at index is the ship number)
private int tiles[];
private int size;
public Sea(int make_size) {
size = make_size;
tiles = new int[size*size];
}
public int size() { return size; }
// This writes out a representation of the sea, but in a funny order
// The idea is to give the player the job of working it out
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
* Doing this in one method makes placing ships much easier
*/
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.
* Unlike isEmpty(x,y), these other methods require that the
* coordinates passed be valid
*/
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;
}
// map the coordinates to the array index
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;
}
}

170
09_Battle/java/Ship.java Normal file
View File

@@ -0,0 +1,170 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.function.Predicate;
/** A single ship, with its position and where it has been hit */
class Ship {
// These are the four directions that ships can be in
public static final int ORIENT_E=0; // goes East from starting position
public static final int ORIENT_SE=1; // goes SouthEast from starting position
public static final int ORIENT_S=2; // goes South from starting position
public static final int ORIENT_SW=3; // goes SouthWest from starting position
private int id; // ship number
private int size; // how many tiles it occupies
private boolean placed; // whether this ship is in the sea yet
private boolean sunk; // whether this ship has been sunk
private ArrayList<Boolean> hits; // which tiles of the ship have been hit
private int startX; // starting position coordinates
private int startY;
private int orientX; // x and y deltas from each tile occupied to the next
private int orientY;
public Ship(int i, int sz) {
id = i; size = sz;
sunk = false; placed = false;
hits = new ArrayList<>(Collections.nCopies(size, false));
}
/** @returns the ship number */
public int id() { return id; }
/** @returns the ship size */
public int size() { return size; }
/* record the ship as having been hit at the given coordinates */
public void hit(int x, int y) {
// need to work out how many tiles from the ship's starting position the hit is at
// that can be worked out from the difference between the starting X coord and this one
// unless the ship runs N-S, in which case use the Y coord instead
int offset;
if (orientX != 0) {
offset = (x - startX) / orientX;
} else {
offset = (y - startY) / orientY;
}
hits.set(offset, true);
// if every tile of the ship has been hit, the ship is sunk
sunk = hits.stream().allMatch(Predicate.isEqual(true));
}
public boolean isSunk() { return sunk; }
// whether the ship has already been hit at the given coordinates
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);
};
// Place the ship in the sea.
// choose a random starting position, and a random direction
// if that doesn't fit, keep picking different positions and directions
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");
}
// Attempt to fit the ship into the sea, starting from a given position and
// in a given direction
// This is by far the most complicated part of the program.
// It will start at the position provided, and attempt to occupy tiles in the
// requested direction. If it does not fit, either because of the edge of the
// sea, or because of ships already in place, it will try to extend the ship
// in the opposite direction instead. If that is not possible, it fails.
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: // east is increasing X coordinate
orientX = 1; orientY = 0;
break;
case ORIENT_SE: // southeast is increasing X and Y
orientX = 1; orientY = 1;
break;
case ORIENT_S: // south is increasing Y
orientX = 0; orientY = 1;
break;
case ORIENT_SW: // southwest is increasing Y but decreasing X
orientX = -1; orientY = 1;
break;
default:
throw new RuntimeException("Invalid orientation " + orient);
}
if (!s.isEmpty(x, y)) return false; // starting position is occupied - placing fails
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)) {
// It is clear to extend the ship forwards
tilesPlaced += 1;
nextX = nextX + orientX;
nextY = nextY + orientY;
} else {
int backX = startX - orientX;
int backY = startY - orientY;
if (extendShip(s, startX, startY, backX, backY)) {
// We can move the ship backwards, so it can be one tile longer
tilesPlaced +=1;
startX = backX;
startY = backY;
} else {
// Could not make it longer or move it backwards
return false;
}
}
}
// Mark in the sea which tiles this ship occupies
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;
}
// Check whether a ship which already occupies the "from" coordinates,
// can also occupy the "to" coordinates.
// They must be within the sea area, empty, and not cause the ship to cross
// over another ship
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;
}
}

View File

@@ -0,0 +1,129 @@
#!/usr/bin/perl
use strict;
use warnings;
#GLOBAL
my %player_bases;
my %computer_bases;
my %player_choices;
my %computer_choices;
&main;
sub main {
&print_intro;
&display_field;
&populate_computer_bases;
&populate_player_bases;
&game_play;
}
sub game_play {
until (keys %computer_bases == 0 || keys %player_bases == 0) {
&player_turn;
if (keys %computer_bases == 0) {
exit;
}
&computer_turn;
}
exit;
}
sub computer_turn {
# There is logic in here to ensure that the computer doesn't try to pick a target it has already picked
my $valid_choice = 0;
until ($valid_choice == 1) {
my $target = int(rand(25)+1);
if (exists $computer_choices{$target}) {
$valid_choice = 0;
}
else {
$valid_choice = 1;
$computer_choices{$target}=1;
if (exists $player_bases{$target}) {
delete($player_bases{$target});
my $size = keys %player_bases;
if ($size > 0) {
print "I GOT YOU. IT WON'T BE LONG NOW. POST $target WAS HIT.\n";
if ($size == 3) { print "YOU HAVE ONLY THREE OUTPOSTS LEFT.\n"};
if ($size == 2) { print "YOU HAVE ONLY TWO OUTPOSTS LEFT.\n"};
if ($size == 1) { print "YOU HAVE ONLY ONE OUTPOSTS LEFT.\n"};
}
else {
print "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT $target. HA, HA, HA.\nBETTER LUCK NEXT TIME\n";
}
}
else {
print "I MISSED YOU, YOU DIRTY RAT. I PICKED $target. YOUR TURN:\n";
}
}
}
}
sub player_turn {
print "WHERE DO YOU WISH TO FIRE YOUR MISSILE\n";
chomp(my $target=<STDIN>);
if (exists $computer_bases{$target}) {
print "YOU GOT ONE OF MY OUTPOSTS!\n";
delete($computer_bases{$target});
my $size = keys %computer_bases;
if ($size == 3) { print "ONE DOWN, THREE TO GO.\n"};
if ($size == 2) { print "TWO DOWN, TWO TO GO.\n"};
if ($size == 1) { print "THREE DOWN, ONE TO GO.\n"};
if ($size == 0) { print "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN\nMY TRANSISTO&S RECUP%RA*E!\n"};
}
else {
print "HA, HA YOU MISSED. MY TURN NOW:\n";
}
}
sub populate_player_bases {
print "WHAT ARE YOUR FOUR POSITIONS\n";
my $positions=<STDIN>;
chomp($positions);
my @positions = split/ /,$positions;
foreach my $base (@positions) {
$player_bases{$base}=0;
}
}
sub display_field {
for my $num (1..25) {
if (length($num) < 2) {
$num = " $num";
}
print "$num ";
if ($num % 5 == 0) {
print "\n";
}
}
}
sub populate_computer_bases {
my $size = 0;
until ($size == 4) {
my $base = int(rand(25)+1);
$computer_bases{$base}=0;
$size = keys %computer_bases;
}
}
sub print_intro {
print " " x 33, "BOMBARDMENT\n";
print " " x 15, " CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n\n";
print "YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU\n";
print "HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.\n";
print "YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.\n";
print "THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.\n\n";
print "THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE\n";
print "OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU.\n";
print "THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS\n";
print "FIRST IS THE WINNER.\n\n";
print "GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!\n\n";
print "TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.\n";
print "\n\n\n\n";
}

View File

@@ -4,10 +4,16 @@ import java.util.Scanner;
* Game of Bombs Away
*
* Based on the Basic game of Bombs Away here
* https://github.com/coding-horror/basic-computer-games/blob/main/12%20Bombs%20Away/bombsaway.bas
* https://github.com/coding-horror/basic-computer-games/blob/main/12_Bombs_Away/bombsaway.bas
*
* Note: The idea was to create a version of the 1970's Basic game in Java, without adding new features.
* Obvious bugs where found have been fixed, but the playability and overlook and feel
* of the game have been faithfully reproduced.
*
* Modern Java coding conventions have been employed and JDK 11 used for maximum compatibility.
*
* Java port by https://github.com/journich
*
* 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 BombsAway {
@@ -116,8 +122,9 @@ public class BombsAway {
private int missions;
private int chanceToHit;
private int chanceToBeHit;
private int percentageHitRateOfGunners;
private boolean liar;
public BombsAway() {
@@ -139,8 +146,9 @@ public class BombsAway {
// Show an introduction the first time the game is played.
case START:
intro();
chanceToHit = 0;
chanceToBeHit = 0;
percentageHitRateOfGunners = 0;
liar = false;
gameState = GAME_STATE.CHOOSE_SIDE;
break;
@@ -267,39 +275,48 @@ public class BombsAway {
break;
case CHOOSE_ENEMY_DEFENCES:
boolean bothWeapons = true;
percentageHitRateOfGunners = 0;
ENEMY_DEFENCES enemyDefences = getEnemyDefences("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3) ? ");
if(enemyDefences == null) {
System.out.println("TRY AGAIN...");
} else {
chanceToBeHit = 35;
switch(enemyDefences) {
case MISSILES:
case GUNS:
bothWeapons = false;
// MISSILES... An extra 35 but cannot specify percentage hit rate for gunners
break;
// fall through on purpose to BOTH since its pretty much identical code other than the chance to hit
// increasing if both weapons are part of the defence.
case GUNS:
// GUNS... No extra 35 but can specify percentage hit rate for gunners
chanceToBeHit = 0;
// fall through (no break) on purpose because remaining code is applicable
// for both GUNS and BOTH options.
case BOTH:
// BOTH... An extra 35 and percentage hit rate for gunners can be specified.
percentageHitRateOfGunners = getNumberFromKeyboard("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ");
if(percentageHitRateOfGunners < 10) {
System.out.println("YOU LIE, BUT YOU'LL PAY...");
}
if(bothWeapons) {
chanceToHit = 35;
liar = true;
}
break;
}
}
gameState = GAME_STATE.PROCESS_FLAK;
// If player didn't lie when entering percentage hit rate of gunners continue with game
// Otherwise shoot down the player.
if(!liar) {
gameState = GAME_STATE.PROCESS_FLAK;
} else {
gameState = GAME_STATE.SHOT_DOWN;
}
break;
// Determine if the players airplan makes it through the Flak.
// Determine if the player's airplane makes it through the Flak.
case PROCESS_FLAK:
double calc = (CHANCE_OF_BEING_SHOT_DOWN_BASE * randomNumber(1));
if ((chanceToHit + percentageHitRateOfGunners) > calc) {
if ((chanceToBeHit + percentageHitRateOfGunners) > calc) {
gameState = GAME_STATE.SHOT_DOWN;
} else {
gameState = GAME_STATE.MADE_IT_THROUGH_FLAK;
@@ -462,7 +479,7 @@ public class BombsAway {
/**
* Check whether a string equals one of a variable number of values
* Useful to check for Y or YES for example
* Comparison is case insensitive.
* Comparison is case-insensitive.
*
* @param text source string
* @param values a range of values to compare against the source string

View File

@@ -45,6 +45,8 @@ function tab(space)
// Main program
async function main()
{
s = 0;
t = 0;
while (1) {
print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.\n");
while (1) {

View File

@@ -0,0 +1,229 @@
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
exit main(@ARGV);
sub main {
$|++;
my $mission = 'y';
# first-level choices will allow us to select the "right" callback
# function to start each mission
my @choices = (\&italy, \&allies, \&japan, \&germany);
# to support being case-insensitive "the right way" we apply the fc()
# function (i.e. "fold case"). This is slightly overkill in this case
# but it's better to stick to good habits.
while (fc($mission // 'n') eq fc('y')) {
say 'YOU ARE A PILOT IN A WORLD WAR II BOMBER.';
my $side = choose(
'WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)', 4);
# arrays start from 0 in Perl, so our starting-from-1 side value
# has to be offset by 1.
$choices[$side - 1]->();
$mission = get_input("\n\n\nANOTHER MISSION (Y OR N)");
}
__exit();
}
# unified exit function, make sure to shame the desertor!
sub __exit ($prefix = '') {
say $prefix, "CHICKEN !!!\n";
exit 0;
}
# unified input gathering. Checks if the input is closed (e.g. because the
# player hit CTRL-D) and __exit()s in case. Gets a prompt for asking a
# question, returns whatever is input (except spaces).
sub get_input ($prompt) {
print "$prompt? ";
defined(my $input = <STDIN>) or __exit("\n");
# remove spaces from the input (including newlines), they are not used
$input =~ s{\s+}{}gmxs;
return $input;
}
# structured choosing function, gets a $prompt for asking a question and
# will iterate asking until the input is a number between 1 and $n_max.
sub choose ($prompt, $n_max) {
while ('necessary') {
my $side = get_input($prompt);
return $side if $side =~ m{\A [1-9]\d* \z}mxs && $side <= $n_max;
say 'TRY AGAIN...';
}
}
# Italy mission has the same structure as Allies and Germany, so it's been
# refactored into a single "multiple()" (pun intended) function, providing
# the right messaging.
sub italy {
return multiple(
'YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)',
q{SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.},
'BE CAREFUL!!!',
q{YOU'RE GOING FOR THE OIL, EH?},
);
}
# Allies mission has the same structure as Italy and Germany, so it's been
# refactored into a single "multiple()" (pun intended) function, providing
# the right messaging.
sub allies {
return multiple(
'AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)',
q{YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.},
q{YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.},
q{YOU'RE CHASING THE BISMARK IN THE NORTH SEA.},
q{YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.},
);
}
# Japan mission is different from the other three and is coded...
# differently. The end game phases are the same as other missions though,
# hence the calls to "direct_hit()" and "endgame()" functions.
sub japan {
say q{YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.};
my $is_first_kamikaze = get_input(q{YOUR FIRST KAMIKAZE MISSION(Y OR N)});
if (fc($is_first_kamikaze) eq fc('n')) {
our $guns_hit_rate = 0;
say '';
return endgame();
}
return direct_hit() if rand(1) > 0.65;
return endgame('fail');
}
# Germany mission has the same structure as Italy and Allies, so it's been
# refactored into a single "multiple()" (pun intended) function, providing
# the right messaging.
sub germany {
return multiple(
"A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\n"
. 'ENGLAND(2), OR FRANCE(3)',
q{YOU'RE NEARING STALINGRAD.},
q{NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.},
q{NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.}
);
}
# This function implements the workhorse for Italy, Allies and Germany
# missions, which all have the same structure. It starts with a $question
# and a few @comments, each commenting every different answer to the
# $question.
sub multiple ($question, @comments) {
my $target = choose($question, scalar @comments);
say "\n", $comments[$target - 1], "\n";
# we gather the number of missions flown so far so that we can
# use it to figure out if *this* mission will be successful. The more
# the missions flown, the higher the probability of success.
my $missions;
while ('necessary') {
$missions = get_input('HOW MANY MISSIONS HAVE YOU FLOWN');
last if $missions < 160;
print 'MISSIONS, NOT MILES...
150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.
NOW THEN, ';
}
say '';
# a little intermediate comment based on the value of $missions
if ($missions < 25) { say "FRESH OUT OF TRANING, EH?\n" }
elsif ($missions >= 100) { say "THAT'S PUSHING THE ODDS!\n" }
# let's roll a 160-faced die and compare to the missions flown so far,
# player might not even have to engage in combat!
return direct_hit() if $missions >= rand(160);
# player didn't get a direct hit on the target, so we provide a
# feedback about how much it was apart. This is part of the story.
my $miss = 2 + int rand(30);
say "MISSED TARGET BY $miss MILES!";
say "NOW YOU'RE REALLY IN FOR IT !!\n";
# here is where the game shows a little "weakness", although it might
# have been done on purpose. We use "our" variables $missiles_hit_rate
# and $guns_hit_rate here because the original BASIC code did not reset
# the associated variables (respectively T and S) at every mission, thus
# leaking state from one mission to the following ones.
#
# In particular, both are leaked to the Japan mission(s), and
# $guns_hit_rate is leaked to future "multiple()" missions that have
# missiles only.
#
# This is what you get when your language only has global variables.
#
# Of course, this might have been done on purpose, and we'll replicate
# this behaviour here because it adds some randomness to the game.
our $missiles_hit_rate = 0;
my $response = choose(
'DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)', 3);
# Apply Gun damage for responses 1 and 3
if ($response != 2) { # there's some guns involved, ask more
say '';
# see comment above as to why we have a "our" variable here
our $guns_hit_rate =
get_input(q{WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)});
# let's normalize the input a bit
$guns_hit_rate = 0 unless $guns_hit_rate =~ m{\A [1-9]\d* \z}mxs;
# a hit rate this low is not reasonable and is immediately punished!
if ($guns_hit_rate < 10) {
say q{YOU LIE, BUT YOU'LL PAY...};
# function endgame() provides the... end game messaging, which is
# also used by the Japan mission, so it's been factored out.
# Passing 'fail' (or any true value) makes sure that is' a
# failure.
return endgame('fail'); # sure failure
}
say '';
}
# Apply missile damage for responses 2 and 3
if ($response > 1 ) {
$missiles_hit_rate = 35; # remember... this is a global variable
}
# hand control over to the "endgame()" refactored function (also shared
# by the Japan mission).
return endgame();
}
sub direct_hit {
my $killed = int rand(100);
say "DIRECT HIT!!!! $killed KILLED.\nMISSION SUCCESSFUL";
return;
}
# This function provides the end game randomization and messages, shared
# across all missions. If passed a true value $fail, it will make sure that
# the outcome is... a failure. This allows coping with a few ad-hoc
# GOTO:s in the original BASIC code, while still preserving a refactored
# code.
sub endgame ($fail = 0) {
our $missiles_hit_rate //= 0;
our $guns_hit_rate //= 0;
$fail ||= ($missiles_hit_rate + $guns_hit_rate) > rand(100);
if ($fail) {
say '* * * * BOOM * * * *
YOU HAVE BEEN SHOT DOWN.....
DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR
LAST TRIBUTE...';
}
else {
say 'YOU MADE IT THROUGH TREMENDOUS FLAK!!';
}
return;
}

View File

@@ -0,0 +1,48 @@
namespace Boxing;
public abstract class AttackStrategy
{
protected const int KnockoutDamageThreshold = 35;
protected readonly Boxer Other;
protected readonly Stack<Action> Work;
private readonly Action _notifyGameEnded;
public AttackStrategy(Boxer other, Stack<Action> work, Action notifyGameEnded)
{
Other = other;
Work = work;
_notifyGameEnded = notifyGameEnded;
}
public void Attack()
{
var punch = GetPunch();
if (punch.IsBestPunch)
{
Other.DamageTaken += 2;
}
Work.Push(punch.Punch switch
{
Punch.FullSwing => FullSwing,
Punch.Hook => Hook,
Punch.Uppercut => Uppercut,
_ => Jab
});
}
protected abstract AttackPunch GetPunch();
protected abstract void FullSwing();
protected abstract void Hook();
protected abstract void Uppercut();
protected abstract void Jab();
protected void RegisterKnockout(string knockoutMessage)
{
Work.Clear();
_notifyGameEnded();
Console.WriteLine(knockoutMessage);
}
protected record AttackPunch(Punch Punch, bool IsBestPunch);
}

45
15_Boxing/csharp/Boxer.cs Normal file
View File

@@ -0,0 +1,45 @@
namespace Boxing;
public class Boxer
{
private int _wins;
private string Name { get; set; } = string.Empty;
public Punch BestPunch { get; set; }
public Punch Vulnerability { get; set; }
public void SetName(string prompt)
{
Console.WriteLine(prompt);
string? name;
do
{
name = Console.ReadLine();
} while (string.IsNullOrWhiteSpace(name));
Name = name;
}
public int DamageTaken { get; set; }
public void ResetForNewRound() => DamageTaken = 0;
public void RecordWin() => _wins += 1;
public bool IsWinner => _wins >= 2;
public override string ToString() => Name;
}
public class Opponent : Boxer
{
public void SetRandomPunches()
{
do
{
BestPunch = (Punch) GameUtils.Roll(4); // B1
Vulnerability = (Punch) GameUtils.Roll(4); // D1
} while (BestPunch == Vulnerability);
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boxing", "Boxing.csproj", "{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,115 @@
using static Boxing.GameUtils;
using static System.Console;
namespace Boxing;
public class OpponentAttackStrategy : AttackStrategy
{
private readonly Opponent _opponent;
public OpponentAttackStrategy(Opponent opponent, Boxer player, Action notifyGameEnded, Stack<Action> work) : base(player, work, notifyGameEnded)
{
_opponent = opponent;
}
protected override AttackPunch GetPunch()
{
var punch = (Punch)Roll(4);
return new AttackPunch(punch, punch == _opponent.BestPunch);
}
protected override void FullSwing() // 720
{
Write($"{_opponent} TAKES A FULL SWING AND");
if (Other.Vulnerability == Punch.FullSwing)
{
ScoreFullSwing();
}
else
{
if (RollSatisfies(60, x => x < 30))
{
WriteLine(" IT'S BLOCKED!");
}
else
{
ScoreFullSwing();
}
}
void ScoreFullSwing()
{
WriteLine(" POW!!!!! HE HITS HIM RIGHT IN THE FACE!");
if (Other.DamageTaken > KnockoutDamageThreshold)
{
Work.Push(RegisterOtherKnockedOut);
}
Other.DamageTaken += 15;
}
}
protected override void Hook() // 810
{
Write($"{_opponent} GETS {Other} IN THE JAW (OUCH!)");
Other.DamageTaken += 7;
WriteLine("....AND AGAIN!");
Other.DamageTaken += 5;
if (Other.DamageTaken > KnockoutDamageThreshold)
{
Work.Push(RegisterOtherKnockedOut);
}
}
protected override void Uppercut() // 860
{
Write($"{Other} IS ATTACKED BY AN UPPERCUT (OH,OH)...");
if (Other.Vulnerability == Punch.Uppercut)
{
ScoreUppercut();
}
else
{
if (RollSatisfies(200, x => x > 75))
{
WriteLine($" BLOCKS AND HITS {_opponent} WITH A HOOK.");
_opponent.DamageTaken += 5;
}
else
{
ScoreUppercut();
}
}
void ScoreUppercut()
{
WriteLine($"AND {_opponent} CONNECTS...");
Other.DamageTaken += 8;
}
}
protected override void Jab() // 640
{
Write($"{_opponent} JABS AND ");
if (Other.Vulnerability == Punch.Jab)
{
ScoreJab();
}
else
{
if (RollSatisfies(7, x => x > 4))
{
WriteLine("BLOOD SPILLS !!!");
ScoreJab();
}
else
{
WriteLine("IT'S BLOCKED!");
}
}
void ScoreJab() => Other.DamageTaken += 5;
}
private void RegisterOtherKnockedOut()
=> RegisterKnockout($"{Other} IS KNOCKED COLD AND {_opponent} IS THE WINNER AND CHAMP!");
}

View File

@@ -0,0 +1,121 @@
using static Boxing.GameUtils;
using static System.Console;
namespace Boxing;
public class PlayerAttackStrategy : AttackStrategy
{
private readonly Boxer _player;
public PlayerAttackStrategy(Boxer player, Opponent opponent, Action notifyGameEnded, Stack<Action> work)
: base(opponent, work, notifyGameEnded) => _player = player;
protected override AttackPunch GetPunch()
{
var punch = GameUtils.GetPunch($"{_player}'S PUNCH");
return new AttackPunch(punch, punch == _player.BestPunch);
}
protected override void FullSwing() // 340
{
Write($"{_player} SWINGS AND ");
if (Other.Vulnerability == Punch.FullSwing)
{
ScoreFullSwing();
}
else
{
if (RollSatisfies(30, x => x < 10))
{
ScoreFullSwing();
}
else
{
WriteLine("HE MISSES");
}
}
void ScoreFullSwing()
{
WriteLine("HE CONNECTS!");
if (Other.DamageTaken > KnockoutDamageThreshold)
{
Work.Push(() => RegisterKnockout($"{Other} IS KNOCKED COLD AND {_player} IS THE WINNER AND CHAMP!"));
}
Other.DamageTaken += 15;
}
}
protected override void Uppercut() // 520
{
Write($"{_player} TRIES AN UPPERCUT ");
if (Other.Vulnerability == Punch.Uppercut)
{
ScoreUpperCut();
}
else
{
if (RollSatisfies(100, x => x < 51))
{
ScoreUpperCut();
}
else
{
WriteLine("AND IT'S BLOCKED (LUCKY BLOCK!)");
}
}
void ScoreUpperCut()
{
WriteLine("AND HE CONNECTS!");
Other.DamageTaken += 4;
}
}
protected override void Hook() // 450
{
Write($"{_player} GIVES THE HOOK... ");
if (Other.Vulnerability == Punch.Hook)
{
ScoreHookOnOpponent();
}
else
{
if (RollSatisfies(2, x => x == 1))
{
WriteLine("BUT IT'S BLOCKED!!!!!!!!!!!!!");
}
else
{
ScoreHookOnOpponent();
}
}
void ScoreHookOnOpponent()
{
WriteLine("CONNECTS...");
Other.DamageTaken += 7;
}
}
protected override void Jab()
{
WriteLine($"{_player} JABS AT {Other}'S HEAD");
if (Other.Vulnerability == Punch.Jab)
{
ScoreJabOnOpponent();
}
else
{
if (RollSatisfies(8, x => x < 4))
{
WriteLine("IT'S BLOCKED.");
}
else
{
ScoreJabOnOpponent();
}
}
void ScoreJabOnOpponent() => Other.DamageTaken += 3;
}
}

View File

@@ -0,0 +1,29 @@
using Boxing;
using static Boxing.GameUtils;
using static System.Console;
WriteLine(new string('\t', 33) + "BOXING");
WriteLine(new string('\t', 15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
WriteLine("{0}{0}{0}BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS){0}", Environment.NewLine);
var opponent = new Opponent();
opponent.SetName("WHAT IS YOUR OPPONENT'S NAME"); // J$
var player = new Boxer();
player.SetName("INPUT YOUR MAN'S NAME"); // L$
PrintPunchDescription();
player.BestPunch = GetPunch("WHAT IS YOUR MANS BEST"); // B
player.Vulnerability = GetPunch("WHAT IS HIS VULNERABILITY"); // D
opponent.SetRandomPunches();
WriteLine($"{opponent}'S ADVANTAGE IS {opponent.BestPunch.ToFriendlyString()} AND VULNERABILITY IS SECRET.");
for (var i = 1; i <= 3; i ++) // R
{
var round = new Round(player, opponent, i);
round.Start();
round.CheckOpponentWin();
round.CheckPlayerWin();
if (round.GameEnded) break;
}
WriteLine("{0}{0}AND NOW GOODBYE FROM THE OLYMPIC ARENA.{0}", Environment.NewLine);

View File

@@ -0,0 +1,9 @@
namespace Boxing;
public enum Punch
{
FullSwing = 1,
Hook = 2,
Uppercut = 3,
Jab = 4
}

96
15_Boxing/csharp/Round.cs Normal file
View File

@@ -0,0 +1,96 @@
namespace Boxing;
class Round
{
private readonly Boxer _player;
private readonly Boxer _opponent;
private readonly int _round;
private Stack<Action> _work = new();
private readonly PlayerAttackStrategy _playerAttackStrategy;
private readonly OpponentAttackStrategy _opponentAttackStrategy;
public bool GameEnded { get; private set; }
public Round(Boxer player, Opponent opponent, int round)
{
_player = player;
_opponent = opponent;
_round = round;
_work.Push(ResetPlayers);
_work.Push(CheckOpponentWin);
_work.Push(CheckPlayerWin);
void NotifyGameEnded() => GameEnded = true;
_playerAttackStrategy = new PlayerAttackStrategy(player, opponent, NotifyGameEnded, _work);
_opponentAttackStrategy = new OpponentAttackStrategy(opponent, player, NotifyGameEnded, _work);
}
public void Start()
{
while (_work.Count > 0)
{
var action = _work.Pop();
// This delay does not exist in the VB code but it makes a bit easier to follow the game.
// I assume the computers at the time were slow enough
// so that they did not need this delay...
Thread.Sleep(300);
action();
}
}
public void CheckOpponentWin()
{
if (_opponent.IsWinner)
{
Console.WriteLine($"{_opponent} WINS (NICE GOING, {_opponent}).");
GameEnded = true;
}
}
public void CheckPlayerWin()
{
if (_player.IsWinner)
{
Console.WriteLine($"{_player} AMAZINGLY WINS!!");
GameEnded = true;
}
}
private void ResetPlayers()
{
_player.ResetForNewRound();
_opponent.ResetForNewRound();
_work.Push(RoundBegins);
}
private void RoundBegins()
{
Console.WriteLine();
Console.WriteLine($"ROUND {_round} BEGINS...");
_work.Push(CheckRoundWinner);
for (var i = 0; i < 7; i++)
{
_work.Push(DecideWhoAttacks);
}
}
private void CheckRoundWinner()
{
if (_opponent.DamageTaken > _player.DamageTaken)
{
Console.WriteLine($"{_player} WINS ROUND {_round}");
_player.RecordWin();
}
else
{
Console.WriteLine($"{_opponent} WINS ROUND {_round}");
_opponent.RecordWin();
}
}
private void DecideWhoAttacks()
{
_work.Push( GameUtils.RollSatisfies(10, x => x > 5) ? _opponentAttackStrategy.Attack : _playerAttackStrategy.Attack );
}
}

35
15_Boxing/csharp/Utils.cs Normal file
View File

@@ -0,0 +1,35 @@
namespace Boxing;
public static class GameUtils
{
private static readonly Random Rnd = new((int) DateTime.UtcNow.Ticks);
public static void PrintPunchDescription() =>
Console.WriteLine($"DIFFERENT PUNCHES ARE: {PunchDesc(Punch.FullSwing)}; {PunchDesc(Punch.Hook)}; {PunchDesc(Punch.Uppercut)}; {PunchDesc(Punch.Jab)}.");
private static string PunchDesc(Punch punch) => $"({(int)punch}) {punch.ToFriendlyString()}";
public static Punch GetPunch(string prompt)
{
Console.WriteLine(prompt);
Punch result;
while (!Enum.TryParse(Console.ReadLine(), out result) || !Enum.IsDefined(typeof(Punch), result))
{
PrintPunchDescription();
}
return result;
}
public static Func<int, int> Roll { get; } = upperLimit => (int) (upperLimit * Rnd.NextSingle()) + 1;
public static bool RollSatisfies(int upperLimit, Predicate<int> predicate) => predicate(Roll(upperLimit));
public static string ToFriendlyString(this Punch punch)
=> punch switch
{
Punch.FullSwing => "FULL SWING",
Punch.Hook => "HOOK",
Punch.Uppercut => "UPPERCUT",
Punch.Jab => "JAB",
_ => throw new ArgumentOutOfRangeException(nameof(punch), punch, null)
};
}

View File

@@ -1,3 +1,13 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
Actually, this is not so much a port as a complete rewrite, making use of
Perl's Posix time functionality. The calendar is for the current year (not
1979), but you can get another year by specifying it on the command line, e.g.
`perl 21_Calendar/perl/calendar.pl 2001`
It *may* even produce output in languages other than English. But the
leftmost column will still be Sunday, even in locales where it is
typically Monday.

130
21_Calendar/perl/calendar.pl Executable file
View File

@@ -0,0 +1,130 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use POSIX qw{ strftime };
use Term::ReadLine; # Prompt and return user input
use Time::Local ();
BEGIN {
*time_gm =
Time::Local->can( 'timegm_modern' ) ||
Time::Local->can( 'timegm' );
}
our $VERSION = '0.000_01';
use constant COLUMN_WIDTH => 6;
use constant SECONDS_PER_DAY => 86400;
binmode STDOUT, ':encoding(utf-8)';
my $year = @ARGV ? $ARGV[0] : ( localtime )[5] + 1900;
my $is_leap_year = is_leap_year( $year );
my $year_len = 365 + $is_leap_year;
print <<'EOD';
CALENDAR
Creative Computing Morristown, New Jersey
EOD
my @mon_len = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
$mon_len[1] += $is_leap_year;
foreach my $month ( 0 .. 11 ) {
my $epoch = time_gm( 0, 0, 0, 1, $month, $year );
my @start_time = gmtime( $epoch );
my ( $week_day, $year_day ) = @start_time[ 6, 7 ];
my $label = strftime( '%B %Y', @start_time );
$label .= ' ' x ( ( 14 - length $label ) / 2 );
printf "\n** %3d ****** %14s ****** %3d **\n",
$year_day, $label, $year_len - $year_day;
{
my $day = 1 + ( 7 - $week_day ) % 7;
foreach my $wd ( 0 .. 6 ) {
my $ep = time_gm( 0, 0, 0, $day + $wd, $month, $year );
printf '%*s', COLUMN_WIDTH, strftime( '%a', gmtime $ep );
}
print "\n";
}
say '*' x ( COLUMN_WIDTH * 7 );
print ' ' x ( COLUMN_WIDTH * $week_day );
my $month_day = 1;
while ( $week_day < 7 ) {
printf '%*d', COLUMN_WIDTH, $month_day++;
$week_day++;
}
print "\n";
$week_day = 0;
while ( $month_day <= $mon_len[$month] ) {
printf '%*d', COLUMN_WIDTH, $month_day++;
$week_day++;
unless ( $week_day % 7 ) {
print "\n";
$week_day = 0;
}
}
print "\n" if $week_day;
}
sub is_leap_year {
my ( $year ) = 1;
return 0 if $year % 4;
return 1 if $year % 100;
return 0 if $year % 400;
return 1;
}
__END__
=head1 TITLE
calendar - Play the game 'Calendar' from Basic Computer Games
=head1 SYNOPSIS
calendar.pl
=head1 DETAILS
This Perl script is a port of calendar, which is the 21st
entry in Basic Computer Games.
Actually, it is not so much a port as a complete rewrite, making use of
Perl's Posix time functionality. The calendar is for the current year
(not 1979), but you can get another year by specifying it on the command
line, e.g.
perl 21_Calendar/perl/calendar.pl 2001
It B<may> even produce output in languages other than English. But the
leftmost column will still be Sunday, even in locales where it is
typically Monday.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :

77
25_Chief/python/Chief.py Normal file
View File

@@ -0,0 +1,77 @@
def print_lightning_bolt():
print('*'*36)
n = 24
while n > 16:
print(' '*n + 'x x')
n -=1
print(' '*16 + 'x xxx')
print(' '*15 + 'x x')
print(' '*14+ 'xxx x')
n -=1
while n > 8:
print(' '*n + 'x x')
n -=1
print(' '*8 + 'xx')
print(' '*7 +'x')
print('*'*36)
def print_solution(n):
print('\n{} plus 3 gives {}. This Divided by 5 equals {}'.format(n, n+3, (n+3)/5))
print('This times 8 gives {}. If we divide 5 and add 5.'.format(( (n+3)/5 )*8 ))
print('We get {}, which, minus 1 equals {}'.format(( ((n+3)/5)*8)/5+5, ((((n+3)/5)*8)/5+5)-1 ))
def Game():
print('\nTake a Number and ADD 3. Now, Divide this number by 5 and')
print('multiply by 8. Now, Divide by 5 and add the same. Subtract 1')
resp = float(input('\nWhat do you have? '))
comp_guess = (((resp - 4)*5)/8)*5 -3
resp2 = input('\nI bet your number was {} was i right(Yes or No)? '.format(comp_guess))
if resp2 == 'Yes' or resp2 == 'YES' or resp2 == 'yes':
print('\nHuh, I Knew I was unbeatable')
print('And here is how i did it')
print_solution(comp_guess)
input('')
else:
resp3 = float(input('\nHUH!! what was you original number? '))
if resp3 == comp_guess:
print('\nThat was my guess, AHA i was right')
print("Shamed to accept defeat i guess, don't worry you can master mathematics too")
print('Here is how i did it')
print_solution(comp_guess)
input('')
else:
print('\nSo you think you\'re so smart, EH?')
print('Now, Watch')
print_solution(resp3)
resp4 = input('\nNow do you believe me? ')
if resp4 == 'Yes' or resp4 == 'YES' or resp4 == 'yes':
print('\nOk, Lets play again sometime bye!!!!')
input('')
else:
print('\nYOU HAVE MADE ME VERY MAD!!!!!')
print("BY THE WRATH OF THE MATHEMATICS AND THE RAGE OF THE GODS")
print("THERE SHALL BE LIGHTNING!!!!!!!")
print_lightning_bolt()
print('\nI Hope you believe me now, for your own sake')
input('')
if __name__ == '__main__':
print('I am CHIEF NUMBERS FREEK, The GREAT INDIAN MATH GOD.')
play = input('\nAre you ready to take the test you called me out for(Yes or No)? ')
if play == 'Yes' or play == 'YES' or play == 'yes':
Game()
else:
print('Ok, Nevermind. Let me go back to my great slumber, Bye')
input('')

226
30_Cube/java/src/Cube.java Normal file
View File

@@ -0,0 +1,226 @@
import java.io.PrintStream;
import java.util.HashSet;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
/**
* Game of Cube
* <p>
* Based on game of Cube at:
* https://github.com/coding-horror/basic-computer-games/blob/main/30_Cube/cube.bas
*
*
*/
public class Cube {
//Current player location
private Location playerLocation;
//Current list of mines
private Set<Location> mines;
//System input / output objects
private PrintStream out;
private Scanner scanner;
//Player's current money
private int money;
/**
* Entry point, creates a new Cube object and calls the play method
* @param args Java execution arguments, not used in application
*/
public static void main(String[] args) {
new Cube().play();
}
public Cube() {
out = System.out;
scanner = new Scanner(System.in);
money = 500;
mines = new HashSet<>(5);
}
/**
* Clears mines and places 5 new mines on the board
*/
private void placeMines() {
mines.clear();
Random random = new Random();
for(int i = 0; i < 5; i++) {
int x = random.nextInt(1,4);
int y = random.nextInt(1,4);
int z = random.nextInt(1,4);
mines.add(new Location(x,y,z));
}
}
/**
* Runs the entire game until the player runs out of money or chooses to stop
*/
public void play() {
out.println("DO YOU WANT TO SEE INSTRUCTIONS? (YES--1,NO--0)");
if(readParsedBoolean()) {
printInstructions();
}
do {
placeMines();
out.println("WANT TO MAKE A WAGER?");
int wager = 0 ;
if(readParsedBoolean()) {
out.println("HOW MUCH?");
do {
wager = Integer.parseInt(scanner.nextLine());
if(wager > money) {
out.println("TRIED TO FOOL ME; BET AGAIN");
}
} while(wager > money);
}
playerLocation = new Location(1,1,1);
while(playerLocation.x + playerLocation.y + playerLocation.z != 9) {
out.println("\nNEXT MOVE");
String input = scanner.nextLine();
String[] stringValues = input.split(",");
if(stringValues.length < 3) {
out.println("ILLEGAL MOVE, YOU LOSE.");
return;
}
int x = Integer.parseInt(stringValues[0]);
int y = Integer.parseInt(stringValues[1]);
int z = Integer.parseInt(stringValues[2]);
Location location = new Location(x,y,z);
if(x < 1 || x > 3 || y < 1 || y > 3 || z < 1 || z > 3 || !isMoveValid(playerLocation,location)) {
out.println("ILLEGAL MOVE, YOU LOSE.");
return;
}
playerLocation = location;
if(mines.contains(location)) {
out.println("******BANG******");
out.println("YOU LOSE!\n\n");
money -= wager;
break;
}
}
if(wager > 0) {
out.printf("YOU NOW HAVE %d DOLLARS\n",money);
}
} while(money > 0 && doAnotherRound());
out.println("TOUGH LUCK!");
out.println("\nGOODBYE.");
}
/**
* Queries the user whether they want to play another round
* @return True if the player decides to play another round,
* False if the player would not like to play again
*/
private boolean doAnotherRound() {
if(money > 0) {
out.println("DO YOU WANT TO TRY AGAIN?");
return readParsedBoolean();
} else {
return false;
}
}
/**
* Prints the instructions to the game, copied from the original code.
*/
public void printInstructions() {
out.println("THIS IS A GAME IN WHICH YOU WILL BE PLAYING AGAINST THE");
out.println("RANDOM DECISION OF THE COMPUTER. THE FIELD OF PLAY IS A");
out.println("CUBE OF SIDE 3. ANY OF THE 27 LOCATIONS CAN BE DESIGNATED");
out.println("BY INPUTTING THREE NUMBERS SUCH AS 2,3,1. AT THE START");
out.println("YOU ARE AUTOMATICALLY AT LOCATION 1,1,1. THE OBJECT OF");
out.println("THE GAME IS TO GET TO LOCATION 3,3,3. ONE MINOR DETAIL:");
out.println("THE COMPUTER WILL PICK, AT RANDOM, 5 LOCATIONS AT WHICH");
out.println("IT WILL PLANT LAND MINES. IF YOU HIT ONE OF THESE LOCATIONS");
out.println("YOU LOSE. ONE OTHER DETAIL: YOU MAY MOVE ONLY ONE SPACE");
out.println("IN ONE DIRECTION EACH MOVE. FOR EXAMPLE: FROM 1,1,2 YOU");
out.println("MAY MOVE TO 2,1,2 OR 1,1,3. YOU MAY NOT CHANGE");
out.println("TWO OF THE NUMBERS ON THE SAME MOVE. IF YOU MAKE AN ILLEGAL");
out.println("MOVE, YOU LOSE AND THE COMPUTER TAKES THE MONEY YOU MAY");
out.println("\n");
out.println("ALL YES OR NO QUESTIONS WILL BE ANSWERED BY A 1 FOR YES");
out.println("OR A 0 (ZERO) FOR NO.");
out.println();
out.println("WHEN STATING THE AMOUNT OF A WAGER, PRINT ONLY THE NUMBER");
out.println("OF DOLLARS (EXAMPLE: 250) YOU ARE AUTOMATICALLY STARTED WITH");
out.println("500 DOLLARS IN YOUR ACCOUNT.");
out.println();
out.println("GOOD LUCK!");
}
/**
* Waits for the user to input a boolean value. This could either be (true,false), (1,0), (y,n), (yes,no), etc.
* By default, it will return false
* @return Parsed boolean value of the user input
*/
private boolean readParsedBoolean() {
String in = scanner.nextLine();
try {
return in.toLowerCase().charAt(0) == 'y' || Boolean.parseBoolean(in) || Integer.parseInt(in) == 1;
} catch(NumberFormatException exception) {
return false;
}
}
/**
* Checks if a move is valid
* @param from The point that the player is at
* @param to The point that the player wishes to move to
* @return True if the player is only moving, at most, 1 location in any direction, False if the move is invalid
*/
private boolean isMoveValid(Location from, Location to) {
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) + Math.abs(from.z - to.z) <= 1;
}
public class Location {
int x,y,z;
public Location(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
/*
For use in HashSet and checking if two Locations are the same
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Location location = (Location) o;
if (x != location.x) return false;
if (y != location.y) return false;
return z == location.z;
}
/*
For use in the HashSet to accordingly index the set
*/
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
result = 31 * result + z;
return result;
}
}
}

View File

@@ -1,59 +1,60 @@
#!/usr/bin/ruby
class DepthCharge
def run_game
output_title()
while true
printf("----------\n")
print_instructions()
setup_game()
printf("\n")
game_loop()
break if ! get_input_another_game()
output_title
loop do
puts "----------"
print_instructions
setup_game
puts
game_loop
break unless get_input_another_game
end
printf("OK. HOPE YOU ENJOYED YOURSELF.\n")
puts "OK. HOPE YOU ENJOYED YOURSELF."
end
def output_title
printf("--- DEPTH CHARGE ---\n")
printf("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n")
printf("\n")
puts "--- DEPTH CHARGE ---"
puts "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
puts
end
def get_input_y_or_n(message)
while true
print(message)
loop do
print message
value = gets.chomp
if (value == 'Y' || value == 'y')
if value.downcase == "y"
return true
elsif value == 'N' || value == 'n'
elsif value.downcase == "n"
return false
end
printf("PLEASE ENTER Y/y OR N/n...\n\n")
puts "PLEASE ENTER Y/y OR N/n..."
puts
end
end
def get_input_positive_integer(message)
while true
print(message)
loop do
print message
value = gets.chomp
if (value == 'd')
debug_game()
if value == "d"
debug_game
next
end
the_input = Integer(value) rescue nil
the_input = Integer(value) rescue 0
if the_input == nil || the_input < 1
printf("PLEASE ENTER A POSITIVE NUMBER\n\n")
if the_input < 1
puts "PLEASE ENTER A POSITIVE NUMBER"
puts
next
end
return the_input
@@ -61,42 +62,39 @@ class DepthCharge
end
def print_instructions
printf( <<~INSTRUCTIONS
YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR
MISSION IS TO DESTROY IT.
puts <<~INSTRUCTIONS
YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR
MISSION IS TO DESTROY IT.
SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A
TRIO OF NUMBERS -- THE FIRST TWO ARE THE
SURFACE COORDINATES (X, Y):
WEST < X < EAST
SOUTH < Y < NORTH
SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A
TRIO OF NUMBERS -- THE FIRST TWO ARE THE
SURFACE COORDINATES (X, Y):
WEST < X < EAST
SOUTH < Y < NORTH
THE THIRD IS THE DEPTH (Z):
SHALLOW < Z < DEEP
THE THIRD IS THE DEPTH (Z):
SHALLOW < Z < DEEP
GOOD LUCK !
GOOD LUCK !
INSTRUCTIONS
)
end
def debug_game
printf("@enemy_x: %d\n", @enemy_x)
printf("@enemy_y: %d\n", @enemy_y)
printf("@enemy_z: %d\n", @enemy_z)
printf("@num_tries: %d\n", @num_tries)
printf("@trial: %d\n", @trial)
printf("\n")
puts "@enemy_x: %d" % @enemy_x
puts "@enemy_y: %d" % @enemy_y
puts "@enemy_z: %d" % @enemy_z
puts "@num_tries: %d" % @num_tries
puts "@trial: %d" % @trial
puts
end
def setup_game
@search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2) + 1
)
setup_enemy()
@num_tries = Integer(Math.log(@search_area_dimension) / Math.log(2) + 1)
setup_enemy
end
def setup_enemy
@@ -113,32 +111,34 @@ GOOD LUCK !
@shot_y = get_input_positive_integer("Y: ")
@shot_z = get_input_positive_integer("Z: ")
if (
(@enemy_x - @shot_x).abs \
+ (@enemy_y - @shot_y).abs \
+ (@enemy_z - @shot_z).abs \
== 0
)
you_win()
distance = (@enemy_x - @shot_x).abs +
(@enemy_y - @shot_y).abs +
(@enemy_z - @shot_z).abs
if distance == 0
you_win
return
else
missed_shot()
missed_shot
end
end
printf("\n")
you_lose()
puts
you_lose
end
def output_game_status
printf("YOU HAVE %d SHOTS REMAINING.\n", @num_tries - @trial + 1)
printf("TRIAL \#%d\n", @trial)
puts "YOU HAVE %d SHOTS REMAINING." % @num_tries - @trial + 1
puts "TRIAL \#%d" % @trial
end
def you_win
printf("\nB O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
puts "\nB O O M ! ! YOU FOUND IT IN %d TRIES!" % @trial
puts
end
def missed_shot
missed_directions = []
@@ -160,14 +160,13 @@ GOOD LUCK !
missed_directions.push('TOO SHALLOW')
end
printf("SONAR REPORTS SHOT WAS: \n")
printf("%s\n", "\t" + missed_directions.join("\n\t"))
puts "SONAR REPORTS SHOT WAS: "
puts "\t#{missed_directions.join("\n\t")}"
end
def you_lose
printf("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n")
printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z)
puts "YOU HAVE BEEN TORPEDOED! ABANDON SHIP!"
puts "THE SUBMARINE WAS AT %d %d %d" % [@enemy_x, @enemy_y, @enemy_z]
end
def get_input_another_game
@@ -176,4 +175,4 @@ GOOD LUCK !
end
game = DepthCharge.new
game.run_game()
game.run_game

38
33_Dice/vbnet/program.vb Normal file
View File

@@ -0,0 +1,38 @@
Imports System
Module Program
Sub Main(args As String())
Const header As String =
" DICE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THIS PROGRAM SIMULATES THE ROLLING OF A
PAIR OF DICE.
YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO
/ROLL/ THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE
A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000."
Console.WriteLine(header)
Dim D6 As New Random()
Dim continuePrompt As String = "YES"
While continuePrompt = "YES"
Console.Write($"{vbCrLf}HOW MANY ROLLS? ")
Dim x As Integer = Convert.ToInt32(Console.ReadLine())
Dim F = Enumerable.Repeat(0, 11).ToList()
For s As Integer = 0 To x - 1
F(D6.Next(6) + D6.Next(6)) += 1
Next
Console.WriteLine($"{vbCrLf}TOTAL SPOTS NUMBER OF TIMES")
For V As Integer = 0 To 10
Console.WriteLine($" {V + 2}{vbTab,-8}{F(V)}")
Next
Console.Write($"{vbCrLf}TRY AGAIN ")
continuePrompt = Console.ReadLine().ToUpper()
End While
End Sub
End Module

View File

@@ -0,0 +1,197 @@
// Flip Flop Game
PrintGameInfo();
bool startNewGame = true;
string[] board = new string[] { "X", "X", "X", "X", "X", "X", "X", "X", "X", "X" };
do
{
int stepsCount = 0;
int lastMove = -1;
int moveIndex;
int gameSum;
double gameEntropyRate = Rnd();
bool toPlay = false;
bool setNewBoard = true;
Print();
Print("HERE IS THE STARTING LINE OF X'S.");
Print();
do
{
bool illegalEntry;
bool equalToLastMove;
if (setNewBoard)
{
PrintNewBoard();
board = new string[] { "X", "X", "X", "X", "X", "X", "X", "X", "X", "X" };
setNewBoard = false;
toPlay = true;
}
stepsCount++;
gameSum = 0;
// Read User's move
do
{
Write("INPUT THE NUMBER? ");
var input = Console.ReadLine();
illegalEntry = !int.TryParse(input, out moveIndex);
if (illegalEntry || moveIndex > 11)
{
illegalEntry = true;
Print("ILLEGAL ENTRY--TRY AGAIN.");
}
}
while (illegalEntry);
if (moveIndex == 11)
{
// Run new game, To start a new game at any point
toPlay = false;
stepsCount = 12;
startNewGame = true;
}
if (moveIndex == 0)
{
// To reset the line to all X, same game
setNewBoard = true;
toPlay = false;
}
if (toPlay)
{
board[moveIndex - 1] = board[moveIndex - 1] == "O" ? "X" : "O";
if (lastMove == moveIndex)
{
equalToLastMove = true;
}
else
{
equalToLastMove = false;
lastMove = moveIndex;
}
do
{
moveIndex = equalToLastMove
? GetMoveIndexWhenEqualeLastMove(moveIndex, gameEntropyRate)
: GetMoveIndex(moveIndex, gameEntropyRate);
board[moveIndex] = board[moveIndex] == "O" ? "X" : "O";
}
while (lastMove == moveIndex && board[moveIndex] == "X");
PrintGameBoard(board);
foreach (var item in board)
{
if (item == "O")
{
gameSum++;
}
}
}
}
while (stepsCount < 12 && gameSum < 10);
if (toPlay)
{
PrintGameResult(gameSum, stepsCount);
Write("DO YOU WANT TO TRY ANOTHER PUZZLE ");
var toContinue = Console.ReadLine();
if (!string.IsNullOrEmpty(toContinue) && toContinue?.ToUpper()[0] == 'N')
{
startNewGame = false;
}
Print();
}
}
while (startNewGame);
void Print(string str = "") => Console.WriteLine(str);
void Write(string value) => Console.Write(value);
string Tab(int pos) => new(' ', pos);
double Rnd() => new Random().NextDouble();
int GetMoveIndex(int moveIndex, double gameEntropyRate)
{
double rate = Math.Tan(gameEntropyRate + moveIndex / gameEntropyRate - moveIndex) - Math.Sin(gameEntropyRate / moveIndex) + 336 * Math.Sin(8 * moveIndex);
return Convert.ToInt32(Math.Floor(10 * (rate - Math.Floor(rate))));
}
int GetMoveIndexWhenEqualeLastMove(int moveIndex, double gameEntropyRate)
{
double rate = 0.592 * (1 / Math.Tan(gameEntropyRate / moveIndex + gameEntropyRate)) / Math.Sin(moveIndex * 2 + gameEntropyRate) - Math.Cos(moveIndex);
return Convert.ToInt32(Math.Floor(10 * (rate - Math.Floor(rate))));
}
void PrintNewBoard()
{
Print("1 2 3 4 5 6 7 8 9 10");
Print("X X X X X X X X X X");
Print();
}
void PrintGameBoard(string[] board)
{
Print("1 2 3 4 5 6 7 8 9 10");
foreach (var item in board)
{
Write($"{item} ");
}
Print();
Print();
}
void PrintGameResult(int gameSum, int stepsCount)
{
if (gameSum == 10)
{
Print($"VERY GOOD. YOU GUESSED IT IN ONLY {stepsCount} GUESSES.");
}
else
{
Print($"TRY HARDER NEXT TIME. IT TOOK YOU {stepsCount} GUESSES.");
}
}
void PrintGameInfo()
{
Print(Tab(32) + "FLIPFLOP");
Print(Tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Print();
Print("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:");
Print();
Print("X X X X X X X X X X");
Print();
Print("TO THIS:");
Print();
Print("O O O O O O O O O O");
Print();
Print("BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE");
Print("LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON");
Print("OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0");
Print("(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE ");
Print("11 (ELEVEN).");
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlipFlop", "FlipFlop.csproj", "{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {108E5099-D7AA-4260-B587-1B1FE1AF6B54}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,162 @@
#A class representing the internal state of a single game of flip flop
# state represents the list of X's (A in the original code)
# guesses represents the number of guesses the user has made (C in the original code)
# seed represents the random seed for an instance of the game (Q in the original code)
Game = Struct.new(:state, :guesses, :seed) do
#The original BASIC program used 1 indexed arrays while Ruby has 0-indexed arrays.
#We can't use 0 indexed arrays for the flip functions or we'll get divide by zero errors.
#These convenience functions allow us to modify and access internal game state in a 1-indexed fashion
def flip_letter(letter_number)
index = letter_number -1
if self.state[index] == 'X'
self.state[index] = 'O'
else
self.state[index] = 'X'
end
end
def letter_at(letter_number)
self.state[letter_number - 1]
end
end
def print_welcome
puts 'FLIPFLOP'.center(72)
puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(72)
puts <<~EOS
THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:
X X X X X X X X X X
TO THIS:
O O O O O O O O O O
BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE
LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON
OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0
(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE
11 (ELEVEN).
EOS
end
def print_starting_message
puts <<~EOS
HERE IS THE STARTING LINE OF X'S.
1 2 3 4 5 6 7 8 9 10
X X X X X X X X X X
EOS
end
#Create a new game with [X,X,X,X,X,X,X,X,X,X] as the state
#0 as the number of guesses and a random seed between 0 and 1
def generate_new_game
Game.new(Array.new(10, 'X'), 0, rand())
end
#Given a game, an index, and a shuffle function, flip one or more letters
def shuffle_board(game, index, shuffle_function)
n = method(shuffle_function).call(game, index)
if game.letter_at(n) == "O"
game.flip_letter(n)
if index == n
n = shuffle_board(game, index, shuffle_function)
end
else
game.flip_letter(n)
end
return n
end
#Shuffle logic copied from original BASIC code
def shuffle_function1(game, index)
r = Math.tan(game.seed + index / game.seed - index) - Math.sin(game.seed / index) + 336 * Math.sin(8 * index)
n = r - r.floor
(10 * n).floor
end
def shuffle_function2(game, index)
r = 0.592 * (1/ Math.tan(game.seed / index + game.seed)) / Math.sin(index * 2 + game.seed) - Math.cos(index)
n = r - r.floor
(10 * n)
end
def play_game
print_starting_message
game = generate_new_game
working_index = nil
loop do
puts "INPUT THE NUMBER"
input = gets.chomp.downcase
#See if the user input a valid integer, fail otherwise
if numeric_input = Integer(input, exception: false)
#If 11 is entered, we're done with this version of the game
if numeric_input == 11
return :restart
end
if numeric_input > 11
puts 'ILLEGAL ENTRY--TRY AGAIN.'
next #illegal entries don't count towards your guesses
end
if working_index == numeric_input
game.flip_letter(numeric_input)
working_index = shuffle_board(game, numeric_input, :shuffle_function2)
#If 0 is entered, we want to reset the state, but not the random seed or number of guesses and keep playing
elsif numeric_input == 0
game.state = Array.new(10, 'X')
elsif game.letter_at(numeric_input) == "O"
game.flip_letter(numeric_input)
if numeric_input == working_index
working_index = shuffle_board(game, numeric_input, :shuffle_function1)
end
else
game.flip_letter(numeric_input)
working_index = shuffle_board(game, numeric_input, :shuffle_function1)
end
else
puts 'ILLEGAL ENTRY--TRY AGAIN.'
next #illegal entries don't count towards your guesses
end
game.guesses += 1
puts '1 2 3 4 5 6 7 8 9 10'
puts game.state.join(' ')
if game.state.all? { |x| x == 'O' }
if game.guesses > 12
puts "TRY HARDER NEXT TIME. IT TOOK YOU #{game.guesses} GUESSES."
else
puts "VERY GOOD. YOU GUESSED IT IN ONLY #{game.guesses} GUESSES."
end
#game is complete
return
end
end
end
#Execution starts
print_welcome
loop do
result = play_game
if result == :restart
next
end
puts 'DO YOU WANT TO TRY ANOTHER PUZZLE'
if gets.chomp.downcase[0] == 'n'
break
end
end

View File

@@ -1,183 +1,202 @@
def GenRandom(C):
C=int(random()*5)+1
def BadInput850():
print ( "\nHAMURABI: I CANNOT DO WHAT YOU WISH.")
print ( "GET YOURSELF ANOTHER STEWARD!!!!!")
Z=99
def BadInput710(S):
print ( "HAMURABI: THINK AGAIN. YOU HAVE ONLY")
print ( S,"BUSHELS OF GRAIN. NOW THEN,")
def BadInput720(A):
print ( "HAMURABI: THINK AGAIN. YOU OWN ONLY",A,"ACRES. NOW THEN,")
def BadInput710(S):
print ( "HAMURABI: THINK AGAIN. YOU HAVE ONLY")
print ( S,"BUSHELS OF GRAIN. NOW THEN,")
def NationalFink():
print ( "DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY")
print ( "BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE")
print ( "ALSO BEEN DECLARED NATIONAL FINK!!!!")
def B_input(promptstring): #emulate BASIC input. It rejects non-numeric values
x=input(promptstring)
while x.isalpha():
x=input("?REDO FROM START\n? ")
return int(x)
from random import random
from random import seed
def gen_random():
return int(random() * 5) + 1
def bad_input_850():
print("\nHAMURABI: I CANNOT DO WHAT YOU WISH.")
print("GET YOURSELF ANOTHER STEWARD!!!!!")
Z = 99
def bad_input_710(S):
print("HAMURABI: THINK AGAIN. YOU HAVE ONLY")
print(S, "BUSHELS OF GRAIN. NOW THEN,")
def bad_input_720(A):
print("HAMURABI: THINK AGAIN. YOU OWN ONLY", A, "ACRES. NOW THEN,")
def national_fink():
print("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY")
print("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE")
print("ALSO BEEN DECLARED NATIONAL FINK!!!!")
def b_input(promptstring): # emulate BASIC input. It rejects non-numeric values
x = input(promptstring)
while x.isalpha():
x = input("?REDO FROM START\n? ")
return int(x)
seed()
title = "HAMURABI"
title = title.rjust(32,' ')
print (title)
title = title.rjust(32, ' ')
print(title)
attribution = "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
attribution = attribution.rjust(15," ")
print (attribution)
print ('\n\n\n')
print ("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA")
print ("FOR A TEN-YEAR TERM OF OFFICE.\n")
D1=0
P1=0
Z=0 #year
P=95 #population
S=2800 #grain stores
H=3000
E=H-S #rats eaten
Y=3 #yield (amount of production from land). Reused as price per acre
A=H/Y #acres of land
I=5 #immigrants
Q=1 #boolean for plague, also input for buy/sell land
D=0 # people
while (Z<11): #line 270. main loop. while the year is less than 11
print ("\n\n\nHAMURABI: I BEG TO REPORT TO YOU")
Z=Z+1 #year
print ( "IN YEAR",Z,",",D,"PEOPLE STARVED,",I,"CAME TO THE CITY,")
P=P+I
if Q==0:
P=int(P/2)
print ("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED.")
print ("POPULATION IS NOW",P)
print ("THE CITY NOW OWNS",A,"ACRES.")
print ("YOU HARVESTED",Y,"BUSHELS PER ACRE.")
print ("THE RATS ATE",E,"BUSHELS.")
print ("YOU NOW HAVE ",S,"BUSHELS IN STORE.\n")
C=int(10*random()) #random number between 1 and 10
Y=C+17
print ("LAND IS TRADING AT",Y,"BUSHELS PER ACRE.")
Q=-99 #dummy value to track status
while Q==-99: #always run the loop once
Q = B_input("HOW MANY ACRES DO YOU WISH TO BUY? ")
if Q<0:
Q = -1 #to avoid the corner case of Q=-99
BadInput850()
Z=99 #jump out of main loop and exit
elif Y*Q>S: #can't afford it
BadInput710(S)
Q=-99 # give'm a second change to get it right
elif Y*Q<=S: #normal case, can afford it
A=A+Q #increase the number of acres by Q
S=S-Y*Q #decrease the amount of grain in store to pay for it
C=0 #WTF is C for?
if Q ==0 and Z!=99: #maybe you want to sell some land?
attribution = attribution.rjust(15, " ")
print(attribution)
print('\n\n\n')
print("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA")
print("FOR A TEN-YEAR TERM OF OFFICE.\n")
D1 = 0
P1 = 0
Z = 0 # year
P = 95 # population
S = 2800 # grain stores
H = 3000
E = H - S # rats eaten
Y = 3 # yield (amount of production from land). Reused as price per acre
A = H / Y # acres of land
I = 5 # immigrants
Q = 1 # boolean for plague, also input for buy/sell land
D = 0 # people
while Z < 11: # line 270. main loop. while the year is less than 11
print("\n\n\nHAMURABI: I BEG TO REPORT TO YOU")
Z = Z + 1 # year
print("IN YEAR", Z, ",", D, "PEOPLE STARVED,", I, "CAME TO THE CITY,")
P = P + I
if Q == 0:
P = int(P / 2)
print("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED.")
print("POPULATION IS NOW", P)
print("THE CITY NOW OWNS", A, "ACRES.")
print("YOU HARVESTED", Y, "BUSHELS PER ACRE.")
print("THE RATS ATE", E, "BUSHELS.")
print("YOU NOW HAVE ", S, "BUSHELS IN STORE.\n")
C = int(10 * random()) # random number between 1 and 10
Y = C + 17
print("LAND IS TRADING AT", Y, "BUSHELS PER ACRE.")
Q = -99 # dummy value to track status
while Q == -99: # always run the loop once
Q = b_input("HOW MANY ACRES DO YOU WISH TO BUY? ")
if Q < 0:
Q = -1 # to avoid the corner case of Q=-99
bad_input_850()
Z = 99 # jump out of main loop and exit
elif Y * Q > S: # can't afford it
bad_input_710(S)
Q = -99 # give'm a second change to get it right
elif Y * Q <= S: # normal case, can afford it
A = A + Q # increase the number of acres by Q
S = S - Y * Q # decrease the amount of grain in store to pay for it
C = 0 # WTF is C for?
if Q == 0 and Z != 99: # maybe you want to sell some land?
Q = -99
while Q==-99:
Q = B_input( "HOW MANY ACRES DO YOU WISH TO SELL? ")
if Q<0:
BadInput850()
Z=99 #jump out of main loop and exit
elif Q<=A:#normal case
A=A-Q # reduce the acres
S=S+Y*Q #add to grain stores
C=0 #still don't know what C is for
else: #Q>A error!
BadInput720()
Q=-99 #reloop
print ("\n")
Q=-99
while Q==-99 and Z!=99:
Q = B_input("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE? ")
if Q<0:
BadInput850()
#REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS?
elif Q>S:
BadInput710
Q=-99 #try again!
else: #we're good. do the transaction
S=S-Q #remove the grain from the stores
C=1 #set the speed of light to 1. jk
print ("\n")
D=-99 #dummy value to force at least one loop
while D == -99 and Z!=99:
D = B_input("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED? ")
if D<0:
BadInput850()
Z=99
elif D>0:
if D>A:
#REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN?
BadInput720(A)
D = -99
elif int(D/2)>S:
#REM *** ENOUGH GRAIN FOR SEED?
BadInput710(S)
while Q == -99:
Q = b_input("HOW MANY ACRES DO YOU WISH TO SELL? ")
if Q < 0:
bad_input_850()
Z = 99 # jump out of main loop and exit
elif Q <= A: # normal case
A = A - Q # reduce the acres
S = S + Y * Q # add to grain stores
C = 0 # still don't know what C is for
else: # Q>A error!
bad_input_720(A)
Q = -99 # reloop
print("\n")
Q = -99
while Q == -99 and Z != 99:
Q = b_input("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE? ")
if Q < 0:
bad_input_850()
# REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS?
elif Q > S:
bad_input_710(S)
Q = -99 # try again!
else: # we're good. do the transaction
S = S - Q # remove the grain from the stores
C = 1 # set the speed of light to 1. jk
print("\n")
D = -99 # dummy value to force at least one loop
while D == -99 and Z != 99:
D = b_input("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED? ")
if D < 0:
bad_input_850()
Z = 99
elif D > 0:
if D > A:
# REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN?
bad_input_720(A)
D = -99
elif D>=10*P:
#REM *** ENOUGH PEOPLE TO TEND THE CROPS?
print ("BUT YOU HAVE ONLY",P,"PEOPLE TO TEND THE FIELDS! NOW THEN,")
D=-99
else: #we're good. decrement the grain store
S=S-int(D/2)
GenRandom(C)
#REM *** A BOUNTIFUL HARVEST!
Y=C
H=D*Y
E=0
GenRandom(C)
if int(C/2)==C/2: #even number. 50/50 chance
#REM *** RATS ARE RUNNING WILD!!
E=int(S/C) #calc losses due to rats, based on previous random number
S=S-E+H #deduct losses from stores
GenRandom(C)
#REM *** LET'S HAVE SOME BABIES
I=int(C*(20*A+S)/P/100+1)
#REM *** HOW MANY PEOPLE HAD FULL TUMMIES?
C=int(Q/20)
#REM *** HORROS, A 15% CHANCE OF PLAGUE
#yeah, should be HORRORS, but left it
Q=int(10*(2*random()-.3))
if P>=C and Z!=99: #if there are some people without full bellies...
#REM *** STARVE ENOUGH FOR IMPEACHMENT?
D=P-C
if D>.45*P:
print ("\nYOU STARVED",D,"PEOPLE IN ONE YEAR!!!")
NationalFink()
Z=99 #exit the loop
P1=((Z-1)*P1+D*100/P)/Z
P=C
D1=D1+D
if Z!=99:
print ( "IN YOUR 10-YEAR TERM OF OFFICE,",P1,"PERCENT OF THE")
print ( "POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF")
print ( D1,"PEOPLE DIED!!")
L=A/P
print ( "YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH")
print ( L,"ACRES PER PERSON.\n")
if (P1>33 or L<7):
NationalFink()
elif (P1>10 or L<9):
print ( "YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV.")
print ( "THE PEOPLE (REMIANING) FIND YOU AN UNPLEASANT RULER, AND,")
print ( "FRANKLY, HATE YOUR GUTS!!")
elif (P1>3 or L<10):
print ( "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT")
print ( "REALLY WASN'T TOO BAD AT ALL. ",int(P*.8*random()),"PEOPLE")
print ( "WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR")
print ( "TRIVIAL PROBLEMS.")
else:
print ( "A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND")
print ( "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n")
for N in range(1,10):
print ( '\a')
elif int(D / 2) > S:
# REM *** ENOUGH GRAIN FOR SEED?
bad_input_710(S)
D = -99
elif D >= 10 * P:
# REM *** ENOUGH PEOPLE TO TEND THE CROPS?
print("BUT YOU HAVE ONLY", P, "PEOPLE TO TEND THE FIELDS! NOW THEN,")
D = -99
else: # we're good. decrement the grain store
S = S - int(D / 2)
C = gen_random()
# REM *** A BOUNTIFUL HARVEST!
Y = C
H = D * Y
E = 0
C = gen_random()
if int(C / 2) == C / 2: # even number. 50/50 chance
# REM *** RATS ARE RUNNING WILD!!
E = int(S / C) # calc losses due to rats, based on previous random number
S = S - E + H # deduct losses from stores
C = gen_random()
# REM *** LET'S HAVE SOME BABIES
I = int(C * (20 * A + S) / P / 100 + 1)
# REM *** HOW MANY PEOPLE HAD FULL TUMMIES?
C = int(Q / 20)
# REM *** HORROS, A 15% CHANCE OF PLAGUE
# yeah, should be HORRORS, but left it
Q = int(10 * (2 * random() - .3))
if P >= C and Z != 99: # if there are some people without full bellies...
# REM *** STARVE ENOUGH FOR IMPEACHMENT?
D = P - C
if D > .45 * P:
print("\nYOU STARVED", D, "PEOPLE IN ONE YEAR!!!")
national_fink()
Z = 99 # exit the loop
P1 = ((Z - 1) * P1 + D * 100 / P) / Z
P = C
D1 = D1 + D
if Z != 99:
print("IN YOUR 10-YEAR TERM OF OFFICE,", P1, "PERCENT OF THE")
print("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF")
print(D1, "PEOPLE DIED!!")
L = A / P
print("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH")
print(L, "ACRES PER PERSON.\n")
if P1 > 33 or L < 7:
national_fink()
elif P1 > 10 or L < 9:
print("YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV.")
print("THE PEOPLE (REMIANING) FIND YOU AN UNPLEASANT RULER, AND,")
print("FRANKLY, HATE YOUR GUTS!!")
elif P1 > 3 or L < 10:
print("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT")
print("REALLY WASN'T TOO BAD AT ALL. ", int(P * .8 * random()), "PEOPLE")
print("WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR")
print("TRIVIAL PROBLEMS.")
else:
print("A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND")
print("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n")
for N in range(1, 10):
print('\a')
print("\nSO LONG FOR NOW.\n")

2
48_High_IQ/d/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
*.obj

214
48_High_IQ/d/README.md Normal file
View File

@@ -0,0 +1,214 @@
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
## Running the code
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
```shell
dmd -dip1000 -run highiq.d
```
[Other compilers](https://dlang.org/download.html) also exist.
## Discussion
The original BASIC game code made use of calculus and clever choises of field IDs to determine the validity of moves.
This is the original layout of IDs over the board:
```
13 14 15
22 23 24
29 30 31 32 33 34 35
38 39 40 41 42 43 44
47 48 49 50 51 52 53
58 59 60
67 68 69
```
This seems not very logical, because, wouldn't it make much more sense to let columns increase with 1 and rows increase
with 10, so you'd get a consistent coordinate system? It seems that the original author's first step in validating
moves was to check that moves jumped from one field over another one onto the next. He did this by making sure that
adjacent IDs alter between even and odd horizontally *and* vertically. So a valid move was always from an even ID to an
even ID *or* from an odd ID to an odd ID. So one of the checks that the BASIC code made was that the sum of both IDs
was even. This is of course not a sufficient test, because moves that jump over three fields are illegal. Therefore the
IDs seem to have been carefully laid oud so that the IDs increase with 1 horizontally, and 9 vertically, everywhere. So
the only valid difference between IDs for a horizontal move was always 2, and the only valid difference for a vertical
move was always 18.
Fact of the matter is, however, that checking for difference is sufficient and the even sum rule is superfluous, so
there is no need for the peculiar distribution of field IDs. Therefore I have chosen the following more logical
distribution:
```
13 14 15
23 24 25
31 32 33 34 35 36 37
41 42 43 44 45 46 47
51 52 53 54 55 56 57
63 64 65
73 74 75
```
As a consequence, the implementation of the game code has become much simpler; Not alone due to one less check, but due
to the fact that conversions between IDs and board coordinates have become unnecessary and thus we can work with a single
representation of the board state.
This version makes a prettier print of the board than the BASIC original, with coordinates for every move, and explains
illegal moves.
## Demo
```
H-I-Q
(After Creative Computing Morristown, New Jersey)
Fields are identified by 2-digit numbers, each
between 1 and 7. Example: the middle field is 44,
the bottom middle is 74.
_1 _2 _3 _4 _5 _6 _7
┌───┬───┬───┐
1_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
2_ │ ■ │ ■ │ ■ │
┌───┬───┼───┼───┼───┼───┬───┐
3_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
4_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
└───┴───┼───┼───┼───┼───┴───┘
6_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
7_ │ ■ │ ■ │ ■ │
└───┴───┴───┘
Move which peg? 23
The peg at 23 has nowhere to go. Try again.
Move which peg? 24
To where? 34
Field 34 is occupied. Try again.
To where? 54
Field 54 is occupied. Try again.
To where? 44
_1 _2 _3 _4 _5 _6 _7
┌───┬───┬───┐
1_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
2_ │ ■ │ │ ■ │
┌───┬───┼───┼───┼───┼───┬───┐
3_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
4_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
└───┴───┼───┼───┼───┼───┴───┘
6_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
7_ │ ■ │ ■ │ ■ │
└───┴───┴───┘
Move which peg? 14
The peg at 14 has nowhere to go. Try again.
Move which peg? 24
There is no peg at 24. Try again.
Move which peg? 44
The peg at 44 has nowhere to go. Try again.
Move which peg? 32
To where? 22
Field 22 is ouside the board. Try again.
To where? 33
Field 33 is occupied. Try again.
To where? 34
_1 _2 _3 _4 _5 _6 _7
┌───┬───┬───┐
1_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
2_ │ ■ │ │ ■ │
┌───┬───┼───┼───┼───┼───┬───┐
3_ │ ■ │ │ │ ■ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
4_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
└───┴───┼───┼───┼───┼───┴───┘
6_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
7_ │ ■ │ ■ │ ■ │
└───┴───┴───┘
Move which peg? 44
To where? 33
You cannot move diagonally. Try again.
To where? 24
_1 _2 _3 _4 _5 _6 _7
┌───┬───┬───┐
1_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
2_ │ ■ │ ■ │ ■ │
┌───┬───┼───┼───┼───┼───┬───┐
3_ │ ■ │ │ │ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
4_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
└───┴───┼───┼───┼───┼───┴───┘
6_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
7_ │ ■ │ ■ │ ■ │
└───┴───┴───┘
Move which peg? 36
To where? 33
You can't jump that far. Try again.
To where? 35
Field 35 is occupied. Try again.
To where? 34
_1 _2 _3 _4 _5 _6 _7
┌───┬───┬───┐
1_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
2_ │ ■ │ ■ │ ■ │
┌───┬───┼───┼───┼───┼───┬───┐
3_ │ ■ │ │ │ ■ │ │ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
4_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
├───┼───┼───┼───┼───┼───┼───┤
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
└───┴───┼───┼───┼───┼───┴───┘
6_ │ ■ │ ■ │ ■ │
├───┼───┼───┤
7_ │ ■ │ ■ │ ■ │
└───┴───┴───┘
Move which peg? 46
To where? 36
You need to jump over another peg. Try again.
To where? down
Field 00 is ouside the board. Try again.
To where?
```

238
48_High_IQ/d/highiq.d Normal file
View File

@@ -0,0 +1,238 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
import std;
void main()
{
enum width = 50;
writeln(center("H-I-Q", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n", width));
writeln(wrap("Fields are identified by 2-digit numbers, each between 1 and 7. " ~
"Example: the middle field is 44, the bottom middle is 74.", width));
Board board;
while (true)
{
while (!board.isGameOver)
{
writeln(board); // Calls board.toString().
board.makeMove;
}
writeln(board, "\nThe game is over.\nYou had ", board.numPegs, " pieces remaining.");
if (board.numPegs == 1)
writeln("Bravo! You made a perfect score!\n",
"Make a screen dump as a record of your accomplishment!\n");
write("Play again? (Yes or No) ");
if (readString.toLower != "yes")
break;
writeln; writeln;
board = Board.init;
}
writeln("\nSo long for now.\n");
}
/// Representation of the game board with pegs.
struct Board
{
enum {outside, taken, empty};
int[8][8] state = [
1: [ 3: taken, 4: taken, 5: taken],
2: [ 3: taken, 4: taken, 5: taken],
3: [1: taken, 2: taken, 3: taken, 4: taken, 5: taken, 6: taken, 7: taken],
4: [1: taken, 2: taken, 3: taken, 4: empty, 5: taken, 6: taken, 7: taken],
5: [1: taken, 2: taken, 3: taken, 4: taken, 5: taken, 6: taken, 7: taken],
6: [ 3: taken, 4: taken, 5: taken],
7: [ 3: taken, 4: taken, 5: taken]
]; // Row 0 and column 0 are unused. Default is 0 (outside).
/// Returns a string representing the board and its current state.
string toString() const
{
dchar[][] lines = [(" _1 _2 _3 _4 _5 _6 _7 ").to!(dchar[]),
(" ┌───┬───┬───┐ ").to!(dchar[]),
(" 1_ │ │ │ │ ").to!(dchar[]),
(" ├───┼───┼───┤ ").to!(dchar[]),
(" 2_ │ │ │ │ ").to!(dchar[]),
(" ┌───┬───┼───┼───┼───┼───┬───┐").to!(dchar[]),
(" 3_ │ │ │ │ │ │ │ │").to!(dchar[]),
(" ├───┼───┼───┼───┼───┼───┼───┤").to!(dchar[]),
(" 4_ │ │ │ │ │ │ │ │").to!(dchar[]),
(" ├───┼───┼───┼───┼───┼───┼───┤").to!(dchar[]),
(" 5_ │ │ │ │ │ │ │ │").to!(dchar[]),
(" └───┴───┼───┼───┼───┼───┴───┘").to!(dchar[]),
(" 6_ │ │ │ │ ").to!(dchar[]),
(" ├───┼───┼───┤ ").to!(dchar[]),
(" 7_ │ │ │ │ ").to!(dchar[]),
(" └───┴───┴───┘ ").to!(dchar[])];
foreach (y, row; state)
foreach (x, field; row)
if (field == taken)
lines[y * 2][x * 4 + 2] = '■';
return lines.join("\n").to!string;
}
/// Tests for possible moves.
bool isGameOver() const
{
foreach (r, row; state)
foreach (c, field; row)
if (field == taken && canMoveFrom(r, c))
return false;
return true;
}
bool canMoveFrom(int row, int col) const
{
if (row >= 3 && state[row - 2][col] == empty) // Up
return state[row - 1][col] == taken;
if (row <= 5 && state[row + 2][col] == empty) // Down
return state[row + 1][col] == taken;
if (col >= 3 && state[row][col - 2] == empty) // Left
return state[row][col - 1] == taken;
if (col <= 5 && state[row][col + 2] == empty) // Right
return state[row][col + 1] == taken;
return false;
}
/// Asks for input, validates the move and updates the board.
void makeMove()
{
bool isOutside(int row, int col)
{
if (row < 1 || row > 7 ||
col < 1 || col > 7 ||
state[row][col] == outside)
{
writeln("Field ", row, col, " is ouside the board. Try again.");
return true;
}
return false;
}
while (true)
{
auto from = (){
while (true)
{
write("\nMove which peg? ");
int field = readInt;
int row = field / 10, col = field % 10;
if (isOutside(row, col))
continue;
if (state[row][col] != taken)
{
writeln("There is no peg at ", field, ". Try again.");
continue;
}
if (!canMoveFrom(row, col))
{
writeln("The peg at ", field, " has nowhere to go. Try again.");
continue;
}
return tuple!("row", "col")(row, col);
}
}();
auto to = (){
while (true)
{
write("To where? ");
int field = readInt;
int row = field / 10, col = field % 10;
if (isOutside(row, col))
continue;
if (state[row][col] == taken)
{
writeln("Field ", field, " is occupied. Try again.");
continue;
}
if (row != from.row && col != from.col)
{
writeln("You cannot move diagonally. Try again.");
continue;
}
if (row == from.row && col == from.col)
{
writeln("You aren't going anywhere. Try again.");
continue;
}
if (abs(row - from.row) + abs(col - from.col) > 2)
{
writeln("You can't jump that far. Try again.");
continue;
}
if (abs(row - from.row) + abs(col - from.col) < 2 ||
state[(row + from.row) / 2][(col + from.col) / 2] != taken)
{
writeln("You need to jump over another peg. Try again.");
continue;
}
return tuple!("row", "col")(row, col);
}
}();
// The move is legal. Update the board state.
state[from.row][from.col] = empty;
state[ to.row][ to.col] = taken;
state[(from.row + to.row) / 2][(from.col + to.col) / 2] = empty;
writeln;
break;
}
}
/// Returns the number of remaining pegs on the board.
int numPegs() const
{
int num = 0;
foreach (row; state)
foreach (field; row)
if (field == taken)
num++;
return num;
}
}
/// Reads an integer from standard input.
int readInt() nothrow
{
try
return readString.to!int;
catch (Exception) // Not an integer.
return 0;
}
/// Reads a string from standard input.
string readString() nothrow
{
try
return trustedReadln.strip;
catch (Exception) // readln throws on I/O and Unicode errors, which we handle here.
return "";
}
/** An @trusted wrapper around readln.
*
* This is the only function that formally requires manual review for memory-safety.
* [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
* which would remove the need to have any @trusted code in this program.
*/
string trustedReadln() @trusted
{
return readln;
}
version (Windows)
{
// Make the Windows console do a better job at printing UTF-8 strings,
// and restore the default upon termination.
import core.sys.windows.windows;
shared static this() @trusted
{
SetConsoleOutputCP(CP_UTF8);
}
shared static ~this() @trusted
{
SetConsoleOutputCP(GetACP);
}
}

View File

@@ -0,0 +1,153 @@
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* Game of HighIQ
* <p>
* Based on the Basic Game of HighIQ Here:
* https://github.com/coding-horror/basic-computer-games/blob/main/48_High_IQ/highiq.bas
*
* No additional functionality has been added
*/
public class HighIQ {
//Game board, as a map of position numbers to their values
private final Map<Integer, Boolean> board;
//Output stream
private final PrintStream out;
//Input scanner to use
private final Scanner scanner;
public HighIQ(Scanner scanner) {
out = System.out;
this.scanner = scanner;
board = new HashMap<>();
//Set of all locations to put initial pegs on
int[] locations = new int[]{
13, 14, 15, 22, 23, 24, 29, 30, 31, 32, 33, 34, 35, 38, 39, 40, 42, 43, 44, 47, 48, 49, 50, 51, 52, 53, 58, 59, 60, 67, 68, 69
};
for (int i : locations) {
board.put(i, true);
}
board.put(41, false);
}
/**
* Plays the actual game, from start to finish.
*/
public void play() {
do {
printBoard();
while (!move()) {
out.println("ILLEGAL MOVE, TRY AGAIN...");
}
} while (!isGameFinished());
int pegCount = 0;
for (Integer key : board.keySet()) {
if (board.getOrDefault(key, false)) {
pegCount++;
}
}
out.println("YOU HAD " + pegCount + " PEGS REMAINING");
if (pegCount == 1) {
out.println("BRAVO! YOU MADE A PERFECT SCORE!");
out.println("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!");
}
}
/**
* Makes an individual move
* @return True if the move was valid, false if the user made an error and the move is invalid
*/
public boolean move() {
out.println("MOVE WHICH PIECE");
int from = scanner.nextInt();
//using the getOrDefault, which will make the statement false if it is an invalid position
if (!board.getOrDefault(from, false)) {
return false;
}
out.println("TO WHERE");
int to = scanner.nextInt();
if (board.getOrDefault(to, true)) {
return false;
}
//Do nothing if they are the same
if (from == to) {
return true;
}
//using the difference to check if the relative locations are valid
int difference = Math.abs(to - from);
if (difference != 2 && difference != 18) {
return false;
}
//check if there is a peg between from and to
if (!board.getOrDefault((to + from) / 2, false)) {
return false;
}
//Actually move
board.put(from,false);
board.put(to,true);
board.put((from + to) / 2, false);
return true;
}
/**
* Checks if the game is finished
* @return True if there are no more moves, False otherwise
*/
public boolean isGameFinished() {
for (Integer key : board.keySet()) {
if (board.get(key)) {
//Spacing is either 1 or 9
//Looking to the right and down from every point, checking for both directions of movement
for (int space : new int[]{1, 9}) {
Boolean nextToPeg = board.getOrDefault(key + space, false);
Boolean hasMovableSpace = !board.getOrDefault(key - space, true) || !board.getOrDefault(key + space * 2, true);
if (nextToPeg && hasMovableSpace) {
return false;
}
}
}
}
return true;
}
public void printBoard() {
for (int i = 0; i < 7; i++) {
for (int j = 11; j < 18; j++) {
out.print(getChar(j + 9 * i));
}
out.println();
}
}
private char getChar(int position) {
Boolean value = board.get(position);
if (value == null) {
return ' ';
} else if (value) {
return '!';
} else {
return 'O';
}
}
}

View File

@@ -0,0 +1,37 @@
import java.util.Scanner;
public class HighIQGame {
public static void main(String[] args) {
printInstructions();
Scanner scanner = new Scanner(System.in);
do {
new HighIQ(scanner).play();
System.out.println("PLAY AGAIN (YES OR NO)");
} while(scanner.nextLine().equalsIgnoreCase("yes"));
}
public static void printInstructions() {
System.out.println("\t\t\t H-I-Q");
System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("HERE IS THE BOARD:");
System.out.println(" ! ! !");
System.out.println(" 13 14 15\n");
System.out.println(" ! ! !");
System.out.println(" 22 23 24\n");
System.out.println("! ! ! ! ! ! !");
System.out.println("29 30 31 32 33 34 35\n");
System.out.println("! ! ! ! ! ! !");
System.out.println("38 39 40 41 42 43 44\n");
System.out.println("! ! ! ! ! ! !");
System.out.println("47 48 49 50 51 52 53\n");
System.out.println(" ! ! !");
System.out.println(" 58 59 60\n");
System.out.println(" ! ! !");
System.out.println(" 67 68 69");
System.out.println("TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD");
System.out.println("WILL BE USED DURING PLAY. REFER TO THE ABOVE ONE FOR PEG");
System.out.println("NUMBERS. OK, LET'S BEGIN.");
}
}

84
51_Hurkle/perl/hurkle.pl Executable file
View File

@@ -0,0 +1,84 @@
#!/usr/bin/perl
use strict;
use warnings;
# global variables
my($GRID) = 10;
my($TRIES) = 5;
# main program starts here
# print instructions
print <<HERE;
Hurkle
Creative Computing Morristown, New Jersey
A Hurkle is hiding on a ${GRID} by ${GRID} grid. Homebase
on the grid is point 0,0 in the southwest corner,
and any point on the grid is designated by a
pair of whole numbers seperated by a comma. The first
number is the horizontal position and the second number
is the vertical position. You must try to
guess the Hurkle's gridpoint. You get ${TRIES} tries.
After each try, I will tell you the approximate
direction to go to look for the Hurkle.
HERE
# The PLAY block is a complete game from start
# to finish. The continue block prints the
# "let's play again" message and then a new
# game is started.
PLAY: while (1) {
my($H1) = int(rand $GRID);
my($H2) = int(rand $GRID);
for my $i (1 .. $TRIES) {
printf(" Guess # %D ? ", $i);
my($G1,$G2);
# The CHECK loop will execute while we
# attempt to collect valid input from
# the player
CHECK: while (1) {
chomp(my $in = <STDIN>);
# Use a regex to attempt to parse out
# two integers separated by a comma.
if ($in =~ m{(\d+)\s*,\s*(\d+)}) {
$G1 = $1; $G2 = $2;
last CHECK;
}
# Input not accepted, please try again
print "Please enter two numbers separated by a comma ? ";
}
if (abs($H1 - $G1) + abs($H2 - $G2) != 0) {
# print directional info
printf("Go %s%s\n\n",
($G2 == $H2 ? '' : $G2 < $H2 ? 'north' : 'south'),
($G1 == $H1 ? '' : $G1 < $H1 ? 'east' : 'west' ),
);
} else {
# win!
printf("\nYou found him in %d tries!\n", $i);
# move to the continue block
next PLAY;
}
} # tries loop
# No more guesses
printf("Sorry, that's %d guesses.\n", $TRIES);
printf("The Hurkle is at %d, %d\n", $H1, $H2);
}
# Execution comes here either from the "next PLAY"
# statement, or by the PLAY block naturally ending
# after the player has lost.
continue {
print "\nLet's play again. Hurkle is hiding.\n\n";
}

View File

@@ -1,3 +1,5 @@
package king53
import kotlin.math.abs
import kotlin.random.Random
import kotlin.system.exitProcess
@@ -22,6 +24,7 @@ fun main() {
with(gameState) {
do {
recalculateLandCost()
displayStatus()
inputLandSale()
@@ -84,6 +87,7 @@ fun loadOldGame(): GameState = GameState().apply {
println(" COME ON, YOUR TERM IN OFFICE IS ONLY $yearsRequired YEARS.")
retry = true
}
} while (retry)
print("HOW MUCH DID YOU HAVE IN THE TREASURY? ")
@@ -115,6 +119,10 @@ fun loadOldGame(): GameState = GameState().apply {
*/
sealed class YearOutcome {
/**
* Display output for the end of the year, for each different possible
* year outcome.
*/
open fun displayConsequences() {
// Default display nothing
}
@@ -132,7 +140,7 @@ sealed class YearOutcome {
object ContinueNextYear : YearOutcome()
class Win(val yearsRequired: Int) : YearOutcome() {
class Win(private val yearsRequired: Int) : YearOutcome() {
override fun displayConsequences() {
// The misspelling of "successfully" is in the original code.
println(
@@ -229,12 +237,18 @@ sealed class YearOutcome {
* Record data, allow data input, and process the simulation for the game.
*/
class GameState(val yearsRequired: Int = 8) {
/**
* The current year. Years start with zero, but we never
* output the current year.
*/
var currentYear = 0
/**
* Keep track of each year's crop loss, so we can report increases.
*/
private var lastYearsCropLoss: Int = 0
/**
* Number of countrymen who have died of either pollution
* or starvation this year.
@@ -535,11 +549,12 @@ class GameState(val yearsRequired: Int = 8) {
the population, but does not affect crop losses.
*/
var cropLoss = ((2000 - landArea) * (rnd + 1.5) / 2.0).toInt()
val cropLossWorse = false
if (foreignWorkers > 0)
print("OF $plantingArea SQ. MILES PLANTED,")
if (plantingArea <= cropLoss)
cropLoss = plantingArea
val cropLossWorse = cropLoss > lastYearsCropLoss
lastYearsCropLoss = cropLoss
println(" YOU HARVESTED ${plantingArea - cropLoss} SQ. MILES OF CROPS.")
if (cropLoss > 0) {

View File

@@ -5,40 +5,37 @@ print ' 'x33 . "LETTER\n";
print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n\n\n";
print "LETTER GUESSING GAME\n"; print "\n";
print "LETTER GUESSING GAME\n\n";
print "I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z.\n";
print "TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES\n";
print "AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.\n";
my $A;
while (1) {
my $L= 65+int(rand(1)*26);
my $G= 0;
print "\n"; print "O.K., I HAVE A LETTER. START GUESSING.\n";
my $letter = 65 + int(rand(26));
my $guesses = 0;
print "\nO.K., I HAVE A LETTER. START GUESSING.\n";
my $answer;
do {
print "\n"; print "WHAT IS YOUR GUESS? ";
$G=$G+1;
chomp($A= <STDIN>);
$A= ord($A);
print "\nWHAT IS YOUR GUESS? ";
$guesses++;
chomp($answer = <STDIN>);
$answer = ord($answer);
print "\n";
if ($A<$L) { print "TOO LOW. TRY A HIGHER LETTER.\n"; }
if ($A>$L) { print "TOO HIGH. TRY A LOWER LETTER.\n"; }
} until($A eq $L);
print "TOO LOW. TRY A HIGHER LETTER.\n" if $answer < $letter;
print "TOO HIGH. TRY A LOWER LETTER.\n" if $answer > $letter;
} until($answer eq $letter);
print "\n"; print "YOU GOT IT IN $G GUESSES!!\n";
print "\nYOU GOT IT IN $guesses GUESSES!!\n";
if ($G<=5) {
if ($guesses <= 5) {
print "GOOD JOB !!!!!\n";
for (my $N=1; $N<=15; $N++) { print chr(7); } #ASCII Bell.
} else {
print chr(7) x 15; # ASCII Bell
} else {
print "BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!\n";
}
print "\n";
print "LET'S PLAN AGAIN.....\n";
}
print "\nLET'S PLAY AGAIN.....\n";
}
exit;

509
55_Life/csharp/.gitignore vendored Normal file
View File

@@ -0,0 +1,509 @@
# Created by https://www.toptal.com/developers/gitignore/api/dotnetcore,rider,visualstudio,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore,rider,visualstudio,visualstudiocode
### DotnetCore ###
# .NET Core build folders
bin/
obj/
# Common node modules locations
/node_modules
/wwwroot/node_modules
### Rider ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# Support for Project snippet scope
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
*.code-workspace
# Local History for Visual Studio Code
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
### VisualStudio Patch ###
# Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,rider,visualstudio,visualstudiocode

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

16
55_Life/csharp/Life.sln Normal file
View File

@@ -0,0 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Life", "Life.csproj", "{28B02688-78D1-4B3E-B998-BCC78C292D03}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

326
55_Life/csharp/Program.cs Normal file
View File

@@ -0,0 +1,326 @@
/*
* LIFE
* An implementation of John Conway's popular cellular automaton
* Ported by Dyego Alekssander Maas
*
* An example pattern would be:
* " * "
* "***"
* "DONE" (indicates that the simulation can start)
*
* You can find patterns to play with here: http://pi.math.cornell.edu/~lipa/mec/lesson6.html
*
* Optionally, you can run this program with the "--wait 1000" argument, the number being the time in milliseconds
* that the application will pause between each iteration. This is enables you to watch the simulation unfolding.
* By default, there is no pause between iterations.
*/
using System.Text;
const int maxWidth = 70;
const int maxHeight = 24;
Console.WriteLine("ENTER YOUR PATTERN:");
var pattern = ReadPattern(limitHeight: maxHeight).ToArray();
var (minX, minY) = FindTopLeftCorner(pattern);
var maxX = maxHeight;
var maxY = maxWidth;
var matrix = new Matrix(height: maxHeight, width: maxWidth);
var simulation = InitializeSimulation(pattern, matrix);
PrintHeader();
ProcessSimulation();
IEnumerable<string> ReadPattern(int limitHeight)
{
for (var i = 0; i < limitHeight; i++)
{
var input = Console.ReadLine();
if (input.ToUpper() == "DONE")
{
yield return string.Empty;
break;
}
// In the original version, BASIC would trim the spaces in the beginning of an input, so the original
// game allowed you to input an '.' before the spaces to circumvent this limitation. This behavior was
// kept for compatibility.
if (input.StartsWith('.'))
yield return input.Substring(1, input.Length - 2);
yield return input;
}
}
(int minX, int minY) FindTopLeftCorner(IEnumerable<string> patternLines)
{
var longestInput = patternLines
.Select((value, index) => (index, value))
.OrderByDescending(input => input.value.Length)
.First();
var centerX = (11 - longestInput.index / 2) - 1;
var centerY = (33 - longestInput.value.Length / 2) - 1;
return (centerX, centerY);
}
void PrintHeader()
{
void PrintCentered(string text)
{
const int pageWidth = 64;
var spaceCount = (pageWidth - text.Length) / 2;
Console.Write(new string(' ', spaceCount));
Console.WriteLine(text);
}
PrintCentered("LIFE");
PrintCentered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
}
Simulation InitializeSimulation(IReadOnlyList<string> inputPattern, Matrix matrixToInitialize) {
var newSimulation = new Simulation();
// translates the pattern to the middle of the simulation and counts initial population
for (var x = 0; x < inputPattern.Count; x++)
{
for (var y = 0; y < inputPattern[x].Length; y++)
{
if (inputPattern[x][y] == ' ') continue;
matrixToInitialize[minX + x, minY + y] = CellState.Stable;
newSimulation.IncreasePopulation();
}
}
return newSimulation;
}
TimeSpan GetPauseBetweenIterations()
{
if (args.Length == 2)
{
var parameter = args[0].ToLower();
if (parameter.Contains("wait"))
{
var value = args[1];
if (int.TryParse(value, out var sleepMilliseconds))
return TimeSpan.FromMilliseconds(sleepMilliseconds);
}
}
return TimeSpan.Zero;
}
void ProcessSimulation()
{
var pauseBetweenIterations = GetPauseBetweenIterations();
var isInvalid = false;
while (true)
{
Console.WriteLine($"GENERATION: {simulation.Generation}\tPOPULATION: {simulation.Population}");
if (isInvalid)
Console.WriteLine("INVALID!");
simulation.StartNewGeneration();
var nextMinX = maxHeight - 1;
var nextMinY = maxWidth - 1;
var nextMaxX = 0;
var nextMaxY = 0;
// prints the empty lines before search area
for (var x = 0; x < minX; x++)
{
Console.WriteLine();
}
// refreshes the matrix and updates search area
for (var x = minX; x < maxX; x++)
{
var printedLine = Enumerable.Repeat(' ', maxWidth).ToList();
for (var y = minY; y < maxY; y++)
{
if (matrix[x, y] == CellState.Dying)
{
matrix[x, y] = CellState.Empty;
continue;
}
if (matrix[x, y] == CellState.New)
{
matrix[x, y] = CellState.Stable;
}
else if (matrix[x, y] != CellState.Stable)
{
continue;
}
printedLine[y] = '*';
nextMinX = Math.Min(x, nextMinX);
nextMaxX = Math.Max(x + 1, nextMaxX);
nextMinY = Math.Min(y, nextMinY);
nextMaxY = Math.Max(y + 1, nextMaxY);
}
Console.WriteLine(string.Join(separator: null, values: printedLine));
}
// prints empty lines after search area
for (var x = maxX + 1; x < maxHeight; x++)
{
Console.WriteLine();
}
Console.WriteLine();
void UpdateSearchArea()
{
minX = nextMinX;
maxX = nextMaxX;
minY = nextMinY;
maxY = nextMaxY;
if (minX < 3)
{
minX = 3;
isInvalid = true;
}
if (maxX > 22)
{
maxX = 22;
isInvalid = true;
}
if (minY < 3)
{
minY = 3;
isInvalid = true;
}
if (maxY > 68)
{
maxY = 68;
isInvalid = true;
}
}
UpdateSearchArea();
for (var x = minX - 1; x < maxX + 2; x++)
{
for (var y = minY - 1; y < maxY + 2; y++)
{
int CountNeighbors()
{
var neighbors = 0;
for (var i = x - 1; i < x + 2; i++)
{
for (var j = y - 1; j < y + 2; j++)
{
if (matrix[i, j] == CellState.Stable || matrix[i, j] == CellState.Dying)
neighbors++;
}
}
return neighbors;
}
var neighbors = CountNeighbors();
if (matrix[x, y] == 0)
{
if (neighbors == 3)
{
matrix[x, y] = CellState.New;
simulation.IncreasePopulation();
}
}
else if (neighbors is < 3 or > 4)
{
matrix[x, y] = CellState.Dying;
}
else
{
simulation.IncreasePopulation();
}
}
}
// expands search area to accommodate new cells
minX--;
minY--;
maxX++;
maxY++;
if (pauseBetweenIterations > TimeSpan.Zero)
Thread.Sleep(pauseBetweenIterations);
}
}
/// <summary>
/// Indicates the state of a given cell in the simulation.
/// </summary>
internal enum CellState
{
Empty = 0,
Stable = 1,
Dying = 2,
New = 3
}
public class Simulation
{
public int Generation { get; private set; }
public int Population { get; private set; }
public void StartNewGeneration()
{
Generation++;
Population = 0;
}
public void IncreasePopulation()
{
Population++;
}
}
/// <summary>
/// This class was created to aid debugging, through the implementation of the ToString() method.
/// </summary>
class Matrix
{
private readonly CellState[,] _matrix;
public Matrix(int height, int width)
{
_matrix = new CellState[height, width];
}
public CellState this[int x, int y]
{
get => _matrix[x, y];
set => _matrix[x, y] = value;
}
public override string ToString()
{
var stringBuilder = new StringBuilder();
for (var x = 0; x < _matrix.GetLength(0); x++)
{
for (var y = 0; y < _matrix.GetLength(1); y++)
{
var character = _matrix[x, y] == 0 ? " ": ((int)_matrix[x, y]).ToString();
stringBuilder.Append(character);
}
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
}

96
62_Mugwump/perl/mugwump.pl Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/perl
use strict;
use warnings;
# global variables defined here
my(@MUGWUMP) = ();
# subroutines defined here
# init_mugwump: pick the random places for the Mugwumps
sub init_mugwump() {
@MUGWUMP = ();
for (1 .. 4) {
push @MUGWUMP, [ int(rand 10), int(rand 10) ];
}
}
# main code starts here
# print introductory text
print <<HERE;
Mugwump
Creative Computing Morristown, New Jersey
The object of this game is to find four Mugwumps
hidden on a 10 by 10 grid. Homebase is position 0,0.
Any guess you make must be two numbers with each
number between 0 and 9, inclusive. First number
is distance to right of homebase and second number
is distance above homebase.
You get 10 tries. After each try, I will tell
you how far you are from each Mugwump.
HERE
# PLAY block implements a complete game, and the
# continue block prints the "let's play again" msg
PLAY: while (1) {
init_mugwump();
TURN: for my $turn (1 .. 10) {
printf("\nTurn no %d -- what is your guess ? ", $turn);
# Note that parsing of input is rudimentary, in keeping with the
# spirit of the original BASIC program. Increased input checks
# would be a good place to start working on this program!
chomp(my $in = <STDIN>);
my($M,$N) = split(/,/,$in);
$M = int($M);
$N = int($N);
for my $i (0 .. $#MUGWUMP) {
# -1 indicates a Mugwump that was already found
next if $MUGWUMP[$i]->[0] == -1;
if ($MUGWUMP[$i]->[0] == $M && $MUGWUMP[$i]->[1] == $N) {
$MUGWUMP[$i]->[0] = -1;
printf("You have found Mugwump %d\n", $i+1);
} else {
my $d = sqrt(($MUGWUMP[$i]->[0] - $M) ** 2 + ($MUGWUMP[$i]->[1] - $N) ** 2);
printf("You are %.1f units away from Mugwump %d\n", $d, $i+1);
}
}
# If a Mugwump still has not been found,
# go to the next turn
for my $j (0 .. $#MUGWUMP) {
if ($MUGWUMP[$j]->[0] != -1) {
next TURN;
}
}
# You win!
printf("You got all of them in %d %s!\n\n", $turn, ($turn == 1 ? 'turn' : 'turns'));
# Pass execution down to the continue block
next PLAY;
} # end of TURN loop
print "\nSorry, that's 10 tries. Here's where they're hiding:\n";
for my $i (0 .. $#MUGWUMP) {
printf("Mugwump %d is at (%d, %d)\n", $i+1, $MUGWUMP[$i]->[0], $MUGWUMP[$i]->[1])
if $MUGWUMP[$i]->[0] != -1;
}
}
continue {
print "\nThat was fun! Let's play again.......\n";
print "Four more Mugwumps are now in hiding.\n\n";
}

View File

@@ -0,0 +1,126 @@
import random
#Class of the Game
class NIM:
def __init__(self):
self.Piles = {
1 : 7,
2 : 5,
3 : 3,
4 : 1
}
def Remove_pegs(self, command):
try:
pile, num = command.split(',')
num = int(num)
pile = int(pile)
except Exception as e:
if 'not enough values' in str(e):
print('\nNot a valid command. Your command should be in the form of "1,3", Try Again\n')
else:
print('\nError, Try again\n')
return None
if self._command_integrity(num, pile) == True:
self.Piles[pile] -= num
else:
print('\nInvalid value of either Peg or Pile\n')
def get_AI_move(self):
possible_pile = []
for k,v in self.Piles.items():
if v != 0:
possible_pile.append(k)
pile = random.choice(possible_pile)
num = random.randint(1,self.Piles[pile])
return pile, num
def _command_integrity(self, num, pile):
if pile <= 4 and pile >= 1:
if num <= self.Piles[pile]:
return True
return False
def print_pegs(self):
for pile, peg in self.Piles.items():
print('Pile {} : {}'.format(pile, 'O '*peg))
def Help(self):
print('-'*10)
print('\nThe Game is player with a number of Piles of Objects("O" == one peg)')
print('\nThe Piles are arranged as given below(Tradional NIM)\n')
self.print_pegs()
print('\nAny Number of of Objects are removed one pile by "YOU" and the machine alternatively')
print('\nOn your turn, you may take all the objects that remain in any pile')
print('but you must take ATLEAST one object')
print('\nAnd you may take objects from only one pile on a single turn.')
print('\nThe winner is defined as the one that picks the last remaning object')
print('-'*10)
def Checkforwin(self):
sum = 0
for k,v in self.Piles.items():
sum += v
if sum == 0:
return True
else:
return False
#main program
if __name__ == "__main__":
#Game initialization
game = NIM()
print('Hello, This is a game of NIM')
help = input('Do You Need Instruction (YES or NO): ')
if help == 'yes' or help == 'YES' or help == 'Yes':
#Printing Game Instruction
game.Help()
#Start game loop
input('\nPress Enter to start the Game:\n')
End = False
while True:
game.print_pegs()
#Players Move
command = input('\nYOUR MOVE - Number of PILE, Number of Object? ')
game.Remove_pegs(command)
End = game.Checkforwin()
if End == True:
print('\nPlayer Wins the Game, Congratulations!!')
input('\nPress any key to exit')
break
#Computers Move
command = game.get_AI_move()
print('\nA.I MOVE - A.I Removed {} pegs from Pile {}'.format(command[1],command[0]))
game.Remove_pegs(str(command[0]) +',' + str(command[1]))
End = game.Checkforwin()
if End == True:
print('\nComputer Wins the Game, Better Luck Next Time\n')
input('Press any key to exit')
break

282
65_Nim/ruby/nim.rb Normal file
View File

@@ -0,0 +1,282 @@
puts "NIM".center(80)
puts"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".center(80)
puts "\n\n\n"
#210 DIM A(100),B(100,10),D(2)
$pileArray = Array.new[100]
$bArray = Array.new
$dArray = Array.new[2]
$winOption = 0 # take-last option
$numberOfPiles = 1
$c = 0
$e = 0
$f = 0
$g = 0
$h = 0
def displayTheRules
puts "THE GAME IS PLAYED WITH A NUMBER OF PILES OF OBJECTS."
puts "ANY NUMBER OF OBJECTS ARE REMOVED FROM ONE PILE BY YOU AND"
puts "THE MACHINE ALTERNATELY. ON YOUR TURN, YOU MAY TAKE"
puts "ALL THE OBJECTS THAT REMAIN IN ANY PILE, BUT YOU MUST"
puts "TAKE AT LEAST ONE OBJECT, AND YOU MAY TAKE OBJECTS FROM"
puts "ONLY ONE PILE ON A SINGLE TURN. YOU MUST SPECIFY WHETHER"
puts "WINNING IS DEFINED AS TAKING OR NOT TAKING THE LAST OBJECT,"
puts "THE NUMBER OF PILES IN THE GAME, AND HOW MANY OBJECTS ARE"
puts "ORIGINALLY IN EACH PILE. EACH PILE MAY CONTAIN A"
puts "DIFFERENT NUMBER OF OBJECTS."
puts "THE MACHINE WILL SHOW ITS MOVE BY LISTING EACH PILE AND THE"
puts "NUMBER OF OBJECTS REMAINING IN THE PILES AFTER EACH OF ITS"
puts "MOVES."
end
def sub1570
$z=0
for i in 1..$numberOfPiles do
if $pileArray[i] != 0 then
return
end
$z=1
return
end
end
def playAnother
put "do you want to play another game";
return gets.strip.ucase == "YES"
end
puts "THIS IS THE GAME OF NIM."
print "DO YOU WANT INSTRUCTIONS?"
240
wantInstructions = gets.strip.upcase
if wantInstructions == "YES" then
displayTheRules
end
#250 IF Z$="NO" THEN 440
#260 IF Z$="no" THEN 440
#270 IF Z$="YES" THEN displayTheRules
#280 IF Z$="yes" THEN displayTheRules
#290 PRINT "PLEASE ANSWER YES OR NO"
#300 GOTO 240
def sub490 # get number of piles
print "ENTER NUMBER OF PILES:"
while $numberOfPiles < 0 && $numberOfPiles <= 100 do
$numberOfPiles = gets.strip.to_i
end
end
def getPileSizes
puts "ENTER PILE SIZES:"
for i in 1..$numberOfPiles do
print i
while true do
$pileArray[i] = gets.strip.to_i
if $pileArray[i] < 2000 && $pileArray[i] > 0 then
break
end
end
end
end
def sub440 # get win option
puts ""
$winOption = 0
while $winOption != 1 && q != 2 do
puts "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST"
$winOption = gets.strip.to_i
end
end
puts "DO YOU WANT TO MOVE FIRST?";
#630 INPUT Q9$
moveFirst = ""
while moveFirst != "YES" && moveFirst != "NO" do
moveFirst = gets.strip.upcase
case moveFirst
when "YES"
yourMove
when "NO"
machineMove
end
end
#640 IF Q9$="YES" THEN 1450
#650 IF Q9$="yes" THEN 1450
#660 IF Q9$="NO" THEN 700
#670 IF Q9$="no" THEN 700
#680 PRINT "PLEASE ANSWER YES OR NO."
#690 GOTO 630
def machineMove
if $winOption == 1 then
sub940 # take last
end
$c=0
for i in 1..$numberOfPiles do
if $pileArray[i] != 0 then 770
$c=$c+1
if $c == 3 then
sub840
end
$dArray[$c-1]=i
end
end
if $c == 2 then
sub920
end
if $pileArray[$dArray[0]] > 1 then
machineWins
end
machineLoses
end
def machineLoses
puts "MACHINE LOSES"
# 810 GOTO playAnother
if playAnother then
sub440 # loop for another
end
end
def machineWins
puts "MACHINE WINS"
# 830 GOTO playAnother
if playAnother then
sub440 # loop for another
end
end
def sub840
$c=0
for i in 1..$numberOfPiles do
if $pileArray[i] > 1 then
sub940
end
if $pileArray[i] == 0 then 890
$c=$c+1
end
if $c/2 != ($c/2).to_i then
machineLoses
end
sub940 # goto
end
end
def sub920
if $pileArray[$dArray[0]] == 1 then
machineWins
end
if $pileArray[$dArray[1]] == 1 then
machineWins
end
end
def sub940
for i in 1..$numberOfPiles do
e=$pileArray[i]
for j in 0..10 do
$f = $e/2
$bArray[i][j] = 2*($f-($f.to_i))
$e = $f.to_i
end
end
end
#for j in 10..0 STEP -1 do
10..0.step(-1).each do|index|
$c=0
$h=0
for i in 1..$numberOfPiles do
if $bArray[i][index] != 0 then
$c=$c+1
if $pileArray[i] > $h then
$h = $pileArray[i]
$g = i
end
end
end
end
if $c/2 != ($c/2).to_i then 1190
end
$e = rand($numberOfPiles).to_i
#if $pileArray[$e] == 0 then 1140
$f = rand($pileArray[$e]).to_i
$pileArray[$e] = $pileArray[$e]-$f
sub1380
$pileArray[$g]=0
for j in 0..10 do
$bArray[$g][index]=0
$c=0
for i in 1..$numberOfPiles do
if $bArray[i][index] != 0 then
$c=$c+1
end
end
$pileArray[$g]=$pileArray[$g]+2*($c/2-($c/2)).to_i*2^j
end
if $winOption == 1 then
sub1380
end
$c=0
for i in 1..$numberOfPiles do
if $pileArray[i]>1 then
sub1380
end
if $pileArray[i] != 0 then
$c=$c+1
end
if $c/2 == ($c/2).to_i then
sub1380
end
$pileArray[$g] == 1 -$pileArray[$g]
def sub1380
puts "PILE SIZE"
for i in 1..$numberOfPiles do
put i
put $pileArray[i]
end
if $winOption == 2 then # avoid take-last option
yourMove
end
sub1570
if $z == 1 then
machineWins
end
end
def yourMove
put "YOUR MOVE - PILE, NUMBER TO BE REMOVED"
# 1460 INPUT x,y
x = gets.strip.to_i
y = gets.strip.to_i
if x > $numberOfPiles then yourMove
if x < 1 then yourMove
if x != INT(x) then yourMove
if y > $pileArray[x] then yourMove
if y < 1 then
yourMove
end
if y != INT(y) then
yourMove
end
$pileArray[x] = $pileArray[x]-y
sub1570 # gosub
if $z == 1 then
machineLoses
end
# 1560 GOTO 700
end
end
end
end
end
end

252
67_One_Check/perl/onecheck.pl Executable file
View File

@@ -0,0 +1,252 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use List::Util qw{ sum }; # Add all its arguments
use Term::ReadLine; # Prompt and return user input
our $VERSION = '0.000_01';
print <<'EOD';
ONE CHECK
Creative Computing Morristown, New Jersey
Solitaire checker puzzle by David Ahl
48 checkers are placed on the 2 outside spaces of a
standard 64-square checkerboard. The object is to
remove as many checkers as possible by diagonal jumps
(as in standard checkers). Use the numbered board to
indicate the square you wish to jump from and to. On
the board printed out on each turn '1' indicates a
checker and '0' an empty square. When you have no
possible jumps remaining, input a '0' in response to
question 'Jump from?'
EOD
while ( 1 ) { # Iterate indefinitely
board_num(); # Display the numerical board.
# Initialize the board, which is a two-dimensional array.
my @board = map { [ ( 1 ) x 8 ] } 0 .. 7; # Initialize to all 1.
for my $row ( 2 .. 5 ) { # Set the center section to 0
for my $col ( 2 .. 5 ) {
$board[$row][$col] = 0;
}
}
print <<'EOD';
And here is the opening position of the checkers.
EOD
board_pos( \@board );
my $moves = 0; # Number of moves made.
# A game proceeds while 'Jump from' is a true value. We make use of
# the fact that of the possible returns, only 0 evaluates false.
while ( my $jump_from = get_input(
'Jump from? ',
sub {
$ARG = lc; # The caller sees this.
return 1 if $ARG eq 'b';
return unless m/ \A [0-9]+ \z /smx;
$ARG += 0; # Numify, because string '00' is true.
return $ARG < 65;
},
"Please enter a number from 0 to 64, or 'b' to re-display the numeric board\n"
)
) {
if ( $jump_from eq 'b' ) {
board_num();
board_pos( \@board );
next;
}
my $jump_to = get_input(
' to? ',
sub { m/ \A [0-9]+ \z /smx },
"Please enter a number from 1 to 64\n",
);
if ( make_move( \@board, $jump_from, $jump_to ) ) {
$moves++;
board_pos( \@board );
} else {
say 'Illegal move. Try again.';
}
}
my $checkers_left = sum( map { sum( @{ $board[$_] } ) } 0 .. 7 );
print <<"EOD";
You made $moves jumps and had $checkers_left pieces
remaining on the board.
EOD
last unless get_yes_no( 'Try again' );
}
print <<'EOD';
O.K. Hope you had fun!!
EOD
# Print the numerical board
sub board_num {
print <<'EOD';
Here is the numerical board:
EOD
foreach my $row ( 0 .. 7 ) {
state $tplt = ( '%3d' x 8 ) . "\n";
my $inx = $row * 8;
printf $tplt, map { $inx + $_ } 1 .. 8;
}
say '';
return;
}
# Print the board position
sub board_pos {
my ( $board ) = @_;
for my $row ( 0 .. 7 ) {
state $tplt = ( '%2d' x 8 ) . "\n";
printf $tplt, @{ $board->[$row] };
}
say '';
return;
}
# Make the move. This is a subroutine for convenience in control flow.
# We return a true value for success, and false for failure.
sub make_move {
my ( $board, $jump_from, $jump_to ) = @_;
$jump_from -= 1;
$jump_to -= 1;
my $from_row = int( $jump_from / 8 ); # Truncates toward 0
my $from_col = $jump_from % 8;
my $to_row = int( $jump_to / 8 ); # Truncates toward 0
my $to_col = $jump_to % 8;
return unless $board->[$from_row][$from_col]; # From must be occupied
return if $board->[$to_row][$to_col]; # To must be vacant
return unless abs( $from_row - $to_row ) == 2; # Must cross two rows
return unless abs( $from_col - $to_col ) == 2; # Must cross two cols
my $over_row = ( $from_row + $to_row ) / 2; # The row jumped over
my $over_col = ( $from_col + $to_col ) / 2; # The col jumped over
$board->[$from_row][$from_col] = # Clear the from cell
$board->[$over_row][$over_col] = 0; # and the jumped cell
$board->[$to_row][$to_col] = 1; # Occupy the to cell
return 1;
}
# Get input from the user. The arguments are:
# * The prompt
# * A reference to validation code. This code receives the response in
# $ARG and returns true for a valid response.
# * A warning to print if the response is not valid. This must end in a
# return.
# The first valid response is returned. An end-of-file terminates the
# script.
sub get_input {
my ( $prompt, $validate, $warning ) = @ARG;
# If no validator is passed, default to one that always returns
# true.
$validate ||= sub { 1 };
# Create the readline object. The 'state' causes the variable to be
# initialized only once, no matter how many times this subroutine is
# called. The do { ... } is a compound statement used because we
# need to tweak the created object before we store it.
state $term = do {
my $obj = Term::ReadLine->new( 'reverse' );
$obj->ornaments( 0 );
$obj;
};
while ( 1 ) { # Iterate indefinitely
# Read the input into the topic variable, localized to prevent
# Spooky Action at a Distance. We exit on undef, which signals
# end-of-file.
exit unless defined( local $ARG = $term->readline( $prompt ) );
# Return the input if it is valid.
return $ARG if $validate->();
# Issue the warning, and go around the merry-go-round again.
warn $warning;
}
}
# Get a yes-or-no answer. The argument is the prompt, which will have
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
# requested to validate the response as beginning with 'y' or 'n',
# case-insensitive. The return is a true value for 'y' and a false value
# for 'n'.
sub get_yes_no {
my ( $prompt ) = @ARG;
state $map_answer = {
n => 0,
y => 1,
};
my $resp = lc get_input(
"$prompt? [y/n]: ",
sub { m/ \A [yn] /smxi },
"Please respond 'y' or 'n'\n",
);
return $map_answer->{ substr $resp, 0, 1 };
}
__END__
=head1 TITLE
one check - Play the game 'One Check' from Basic Computer Games
=head1 SYNOPSIS
one check.pl
=head1 DETAILS
This Perl script is a port of onecheck.
This is a solitaire game played on a checker board, where the object is
to eliminate as many checkers as possible by making diagonal jumps and
removing the jumped checkers.
It is pretty much a straight port of the BASIC original.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :

View File

@@ -1,3 +1,11 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
This Perl script is a port of orbit, which is the 68th entry in Basic
Computer Games.
In this game you are a planetary defense gunner trying to shoot down a
cloaked Romulan ship before it can escape.
This is pretty much a straight port of the BASIC into idiomatic Perl.

239
68_Orbit/perl/orbit.pl Executable file
View File

@@ -0,0 +1,239 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use Term::ReadLine; # Prompt and return user input
our $VERSION = '0.000_01';
use constant PI => atan2( 0, -1 );
use constant DEG_TO_RAD => atan2( 0, -1 ) / 180;
print <<'EOD';
ORBIT
Creative Computing Morristown, New Jersey
Somewhere above your planet is a Romulan ship.
The ship is in a constant polar orbit. Its
distance from the center of your planet is from
10,000 to 30,000 miles and at its present velocity can
circle your planet once every 12 to 36 hours.
Unfortunately, they are using a cloaking device so
you are unable to see them, but with a special
instrument you can tell how near their ship your
photon bomb exploded. You have seven hours until they
have built up sufficient power in order to escape
your planet's gravity.
Your planet has enough power to fire one bomb an hour.
At the beginning of each hour you will be asked to give an
angle (between 0 and 360) and a distance in units of
100 miles (between 100 and 300), after which your bomb's
distance from the enemy ship will be given.
An explosion within 5,000 miles of the Romulan ship
will destroy it.
Below is a diagram to help you visualize your plight.
90
0000000000000
0000000000000000000
000000 000000
00000 00000
00000 XXXXXXXXXXX 00000
00000 XXXXXXXXXXXXX 00000
0000 XXXXXXXXXXXXXXX 0000
0000 XXXXXXXXXXXXXXXXX 0000
0000 XXXXXXXXXXXXXXXXXXX 0000
180<== 00000 XXXXXXXXXXXXXXXXXXX 00000 ==>0
0000 XXXXXXXXXXXXXXXXXXX 0000
0000 XXXXXXXXXXXXXXXXX 0000
0000 XXXXXXXXXXXXXXX 0000
00000 XXXXXXXXXXXXX 00000
00000 XXXXXXXXXXX 00000
00000 00000
000000 000000
0000000000000000000
0000000000000
270
X - Your planet
O - The orbit of the Romulan ship
On the above diagram, the Romulan ship is circling
counterclockwise around your planet. Don't forget that
without sufficient power the Romulan ship's altitude
and orbital rate will remain constant.
Good luck. The Federation is counting on you.
EOD
while ( 1 ) { # Iterate indefinitely
my $romulan_angle = int( 360 * rand() );
my $romulan_distance = int( 200 * rand() + 200 );
my $romulan_velocity = int( 20 * rand() + 10 );
my $hour = 0;
while ( 1 ) { # Iterate indefinitely
$hour++;
print <<"EOD";
This is hour $hour, at what angle do you wish to send
EOD
my $bomb_angle = get_input(
'do you wish to send your photon bomb? ',
sub { m/ \A [0-9]+ \z /smx },
"Please enter an integer angle in degrees\n",
);
say '';
my $bomb_distance = get_input(
'How far out do you wish to detonate it? ',
sub { m/ \A [0-9]+ \z /smx },
"Please enter an integer distance in hundreds of miles\n",
);
$romulan_angle = ( $romulan_angle + $romulan_velocity ) % 360;
my $miss_angle = abs( $romulan_angle - $bomb_angle );
$miss_angle = 360 - $miss_angle if $miss_angle >= 180;
my $miss_distance = int sqrt(
$romulan_distance * $romulan_distance +
$bomb_distance * $bomb_distance -
2 * $romulan_distance * $bomb_distance *
cos( $miss_angle * DEG_TO_RAD ) );
print <<"EOD";
Your photon bomb exploded $miss_distance*10^2 miles from the
Romulan ship.
EOD
if ( $miss_distance <= 50 ) {
say "\nYou have successfully completed your mission.";
last;
} elsif ( $hour > 6 ) {
say "\nYou have allowed the Romulans to escape.";
last;
}
}
say "\nAnother Romulan ship has gone into orbit.";
last unless get_yes_no( 'Do you wish to try to destroy it' );
}
print <<'EOD';
Good bye.
EOD
# Get input from the user. The arguments are:
# * The prompt
# * A reference to validation code. This code receives the response in
# $ARG and returns true for a valid response.
# * A warning to print if the response is not valid. This must end in a
# return.
# The first valid response is returned. An end-of-file terminates the
# script.
sub get_input {
my ( $prompt, $validate, $warning ) = @ARG;
# If no validator is passed, default to one that always returns
# true.
$validate ||= sub { 1 };
# Create the readline object. The 'state' causes the variable to be
# initialized only once, no matter how many times this subroutine is
# called. The do { ... } is a compound statement used because we
# need to tweak the created object before we store it.
state $term = do {
my $obj = Term::ReadLine->new( 'reverse' );
$obj->ornaments( 0 );
$obj;
};
while ( 1 ) { # Iterate indefinitely
# Read the input into the topic variable, localized to prevent
# Spooky Action at a Distance. We exit on undef, which signals
# end-of-file.
exit unless defined( local $ARG = $term->readline( $prompt ) );
# Return the input if it is valid.
return $ARG if $validate->();
# Issue the warning, and go around the merry-go-round again.
warn $warning;
}
}
# Get a yes-or-no answer. The argument is the prompt, which will have
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
# requested to validate the response as beginning with 'y' or 'n',
# case-insensitive. The return is a true value for 'y' and a false value
# for 'n'.
sub get_yes_no {
my ( $prompt ) = @ARG;
state $map_answer = {
n => 0,
y => 1,
};
my $resp = lc get_input(
"$prompt? [y/n]: ",
sub { m/ \A [yn] /smxi },
"Please respond 'y' or 'n'\n",
);
return $map_answer->{ substr $resp, 0, 1 };
}
__END__
=head1 TITLE
orbit - Play the game 'Orbit' from Basic Computer Games
=head1 SYNOPSIS
orbit.pl
=head1 DETAILS
This Perl script is a port of orbit, which is the 68th entry in Basic
Computer Games.
In this game you are a planetary defense gunner trying to shoot down a
cloaked Romulan ship before it can escape.
This is pretty much a straight port of the BASIC into idiomatic Perl.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :

View File

@@ -0,0 +1,131 @@
using System.Text;
namespace Pizza
{
internal class CustomerMap
{
private readonly int _mapSize;
private readonly string[,] _customerMap;
public CustomerMap(int mapSize)
{
_mapSize = mapSize;
_customerMap = GenerateCustomerMap();
}
/// <summary>
/// Gets customer on position X, Y.
/// </summary>
/// <param name="x">Represents X position.</param>
/// <param name="y">Represents Y position.</param>
/// <returns>If positions is valid then returns customer name otherwise returns empty string.</returns>
public string GetCustomerOnPosition(int x, int y)
{
if(IsPositionOutOfRange(x, y))
{
return string.Empty;
}
return _customerMap[y, x];
}
/// <summary>
/// Overridden ToString for getting text representation of customers map.
/// </summary>
/// <returns>Text representation of customers map.</returns>
public override string ToString()
{
int verticalSpace = 4;
int horizontalSpace = 5;
var mapToDisplay = new StringBuilder();
AppendXLine(mapToDisplay, horizontalSpace);
for (int i = _customerMap.GetLength(0) - 1; i >= 0; i--)
{
mapToDisplay.AppendLine("-", verticalSpace);
mapToDisplay.Append($"{i + 1}");
mapToDisplay.Append(' ', horizontalSpace);
for (var j = 0; j < _customerMap.GetLength(1); j++)
{
mapToDisplay.Append($"{_customerMap[i, j]}");
mapToDisplay.Append(' ', horizontalSpace);
}
mapToDisplay.Append($"{i + 1}");
mapToDisplay.Append(' ', horizontalSpace);
mapToDisplay.Append(Environment.NewLine);
}
mapToDisplay.AppendLine("-", verticalSpace);
AppendXLine(mapToDisplay, horizontalSpace);
return mapToDisplay.ToString();
}
/// <summary>
/// Checks if position is out of range or not.
/// </summary>
/// <param name="x">Represents X position.</param>
/// <param name="y">Represents Y position.</param>
/// <returns>True if position is out of range otherwise false.</returns>
private bool IsPositionOutOfRange(int x, int y)
{
return
x < 0 || x > _mapSize - 1 ||
y < 0 || y > _mapSize - 1;
}
/// <summary>
/// Generates array which represents customers map.
/// </summary>
/// <returns>Returns customers map.</returns>
private string[,] GenerateCustomerMap()
{
string[,] customerMap = new string[_mapSize, _mapSize];
string[] customerNames = GetCustomerNames(_mapSize * _mapSize);
int currentCustomerNameIndex = 0;
for (int i = 0; i < customerMap.GetLength(0); i++)
{
for (int j = 0; j < customerMap.GetLength(1); j++)
{
customerMap[i, j] = customerNames[currentCustomerNameIndex++].ToString();
}
}
return customerMap;
}
/// <summary>
/// Generates customer names. Names are represented by alphanumerics from 'A'. Name of last customer depends on passed parameter.
/// </summary>
/// <param name="numberOfCustomers">How many customers need to be generated.</param>
/// <returns>List of customer names.</returns>
private static string[] GetCustomerNames(int numberOfCustomers)
{
// returns ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P"];
return Enumerable.Range(65, numberOfCustomers).Select(c => ((Char)c).ToString()).ToArray();
}
/// <summary>
/// Appends line with X coordinates.
/// </summary>
/// <param name="mapToDisplay">Current map where a new line will be appended.</param>
/// <param name="horizontalSpace">Number of horizontal delimiters which will be added between each coordination.</param>
private void AppendXLine(StringBuilder mapToDisplay, int horizontalSpace)
{
mapToDisplay.Append(' ');
mapToDisplay.Append('-', horizontalSpace);
for (var i = 0; i < _customerMap.GetLength(0); i++)
{
mapToDisplay.Append($"{i + 1}");
mapToDisplay.Append('-', horizontalSpace);
}
mapToDisplay.Append(Environment.NewLine);
}
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31912.275
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pizza", "Pizza.csproj", "{6AABE938-39C4-4661-AEA5-23CECA1DFEE1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6AABE938-39C4-4661-AEA5-23CECA1DFEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AABE938-39C4-4661-AEA5-23CECA1DFEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AABE938-39C4-4661-AEA5-23CECA1DFEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AABE938-39C4-4661-AEA5-23CECA1DFEE1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8F7E9FAD-38C5-47A2-B5CA-2B6E5947B982}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,296 @@
namespace Pizza
{
internal class PizzaGame
{
private const int CustomerMapSize = 4;
private readonly CustomerMap _customerMap = new CustomerMap(CustomerMapSize);
/// <summary>
/// Starts game. Main coordinator for pizza game.
/// It is responsible for showing information, getting data from user and starting to delivery pizza.
/// </summary>
public void Play()
{
ShowHeader();
string playerName = GetPlayerName();
ShowIntroduction(playerName);
ShowMap();
if (AskForMoreDirections())
{
ShowMoreDirections(playerName);
var playerUnderstands = AskIfPlayerUnderstand();
if (!playerUnderstands)
{
return;
}
}
StartDelivery(playerName);
EndDelivery(playerName);
}
/// <summary>
/// Starts with pizza delivering to customers.
/// Every 5 deliveries it is asking user whether want to continue in delivering.
/// </summary>
/// <param name="playerName">Player name which was filled by user.</param>
private void StartDelivery(string playerName)
{
var numberOfDeliveredPizzas = 0;
while (true)
{
numberOfDeliveredPizzas++;
string deliverPizzaToCustomer = GetRandomCustomer();
WriteEmptyLine();
Console.WriteLine($"HELLO {playerName}'S PIZZA. THIS IS {deliverPizzaToCustomer}.");
Console.WriteLine("\tPLEASE SEND A PIZZA.");
DeliverPizzaByPlayer(playerName, deliverPizzaToCustomer);
if (numberOfDeliveredPizzas % 5 == 0)
{
bool playerWantToDeliveryMorePizzas = AskQuestionWithYesNoResponse("DO YOU WANT TO DELIVER MORE PIZZAS?");
if (!playerWantToDeliveryMorePizzas)
{
WriteEmptyLine();
break;
}
}
}
}
/// <summary>
/// Gets random customer for which pizza should be delivered.
/// </summary>
/// <returns>Customer name.</returns>
private string GetRandomCustomer()
{
int randomPositionOnX = Random.Shared.Next(0, CustomerMapSize);
int randomPositionOnY = Random.Shared.Next(0, CustomerMapSize);
return _customerMap.GetCustomerOnPosition(randomPositionOnX, randomPositionOnY);
}
/// <summary>
/// Delivers pizza to customer by player. It verifies whether player was delivering pizza to correct customer.
/// </summary>
/// <param name="playerName">Player name which was filled by user.</param>
/// <param name="deliverPizzaToCustomer">Customer name which order pizza.</param>
private void DeliverPizzaByPlayer(string playerName, string deliverPizzaToCustomer)
{
while (true)
{
string userInput = GetPlayerInput($"\tDRIVER TO {playerName}: WHERE DOES {deliverPizzaToCustomer} LIVE?");
var deliveredToCustomer = GetCustomerFromPlayerInput(userInput);
if (string.IsNullOrEmpty(deliveredToCustomer))
{
deliveredToCustomer = "UNKNOWN CUSTOMER";
}
if (deliveredToCustomer.Equals(deliverPizzaToCustomer))
{
Console.WriteLine($"HELLO {playerName}. THIS IS {deliverPizzaToCustomer}, THANKS FOR THE PIZZA.");
break;
}
Console.WriteLine($"THIS IS {deliveredToCustomer}. I DID NOT ORDER A PIZZA.");
Console.WriteLine($"I LIVE AT {userInput}");
}
}
/// <summary>
/// Gets customer name by user input with customer coordinations.
/// </summary>
/// <param name="userInput">Input from users - it should represent customer coordination separated by ','.</param>
/// <returns>If coordinations are correct and customer exists then returns true otherwise false.</returns>
private string GetCustomerFromPlayerInput(string userInput)
{
var pizzaIsDeliveredToPosition = userInput?
.Split(',')
.Select(i => int.TryParse(i, out var customerPosition) ? (customerPosition - 1) : -1)
.Where(i => i != -1)
.ToArray() ?? Array.Empty<int>();
if (pizzaIsDeliveredToPosition.Length != 2)
{
return string.Empty;
}
return _customerMap.GetCustomerOnPosition(pizzaIsDeliveredToPosition[0], pizzaIsDeliveredToPosition[1]);
}
/// <summary>
/// Shows game header in console.
/// </summary>
private void ShowHeader()
{
Console.WriteLine("PIZZA".PadLeft(22));
Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
WriteEmptyLine(3);
Console.WriteLine("PIZZA DELIVERY GAME");
WriteEmptyLine();
}
/// <summary>
/// Asks user for name which will be used in game.
/// </summary>
/// <returns>Player name.</returns>
private string GetPlayerName()
{
return GetPlayerInput("WHAT IS YOUR FIRST NAME:");
}
/// <summary>
/// Shows game introduction in console
/// </summary>
/// <param name="playerName">Player name which was filled by user.</param>
private void ShowIntroduction(string playerName)
{
Console.WriteLine($"HI, {playerName}. IN THIS GAME YOU ARE TO TAKE ORDERS");
Console.WriteLine("FOR PIZZAS. THEN YOU ARE TO TELL A DELIVERY BOY");
Console.WriteLine("WHERE TO DELIVER THE ORDERED PIZZAS.");
WriteEmptyLine(2);
}
/// <summary>
/// Shows customers map in console. In this method is used overridden method 'ToString' for getting text representation of customers map.
/// </summary>
private void ShowMap()
{
Console.WriteLine("MAP OF THE CITY OF HYATTSVILLE");
WriteEmptyLine();
Console.WriteLine(_customerMap.ToString());
Console.WriteLine("THE OUTPUT IS A MAP OF THE HOMES WHERE");
Console.WriteLine("YOU ARE TO SEND PIZZAS.");
WriteEmptyLine();
Console.WriteLine("YOUR JOB IS TO GIVE A TRUCK DRIVER");
Console.WriteLine("THE LOCATION OR COORDINATES OF THE");
Console.WriteLine("HOME ORDERING THE PIZZA.");
WriteEmptyLine();
}
/// <summary>
/// Asks user if needs more directions.
/// </summary>
/// <returns>True if user need more directions otherwise false.</returns>
private bool AskForMoreDirections()
{
var playerNeedsMoreDirections = AskQuestionWithYesNoResponse("DO YOU NEED MORE DIRECTIONS?");
WriteEmptyLine();
return playerNeedsMoreDirections;
}
/// <summary>
/// Shows more directions.
/// </summary>
/// <param name="playerName">Player name which was filled by user.</param>
private void ShowMoreDirections(string playerName)
{
Console.WriteLine("SOMEBODY WILL ASK FOR A PIZZA TO BE");
Console.WriteLine("DELIVERED. THEN A DELIVERY BOY WILL");
Console.WriteLine("ASK YOU FOR THE LOCATION.");
Console.WriteLine("\tEXAMPLE:");
Console.WriteLine("THIS IS J. PLEASE SEND A PIZZA.");
Console.WriteLine($"DRIVER TO {playerName}. WHERE DOES J LIVE?");
Console.WriteLine("YOUR ANSWER WOULD BE 2,3");
}
/// <summary>
/// Asks user if understands to instructions.
/// </summary>
/// <returns>True if user understand otherwise false.</returns>
private bool AskIfPlayerUnderstand()
{
var playerUnderstands = AskQuestionWithYesNoResponse("UNDERSTAND?");
if (!playerUnderstands)
{
Console.WriteLine("THIS JOB IS DEFINITELY TOO DIFFICULT FOR YOU. THANKS ANYWAY");
return false;
}
WriteEmptyLine();
Console.WriteLine("GOOD. YOU ARE NOW READY TO START TAKING ORDERS.");
WriteEmptyLine();
Console.WriteLine("GOOD LUCK!!");
WriteEmptyLine();
return true;
}
/// <summary>
/// Shows message about ending delivery in console.
/// </summary>
/// <param name="playerName">Player name which was filled by user.</param>
private void EndDelivery(string playerName)
{
Console.WriteLine($"O.K. {playerName}, SEE YOU LATER!");
WriteEmptyLine();
}
/// <summary>
/// Gets input from user.
/// </summary>
/// <param name="question">Question which is displayed in console.</param>
/// <returns>User input.</returns>
private string GetPlayerInput(string question)
{
Console.Write($"{question} ");
while (true)
{
var userInput = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(userInput))
{
return userInput;
}
}
}
/// <summary>
/// Asks user with required resposne 'YES', 'Y, 'NO', 'N'.
/// </summary>
/// <param name="question">Question which is displayed in console.</param>
/// <returns>True if user write 'YES', 'Y'. False if user write 'NO', 'N'.</returns>
private static bool AskQuestionWithYesNoResponse(string question)
{
var possitiveResponse = new string[] { "Y", "YES" };
var negativeResponse = new string[] { "N", "NO" };
var validUserInputs = possitiveResponse.Concat(negativeResponse);
Console.Write($"{question} ");
string? userInput;
while (true)
{
userInput = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(userInput) && validUserInputs.Contains(userInput.ToUpper()))
{
break;
}
Console.Write($"'YES' OR 'NO' PLEASE, NOW THEN, {question} ");
}
return possitiveResponse.Contains(userInput.ToUpper());
}
/// <summary>
/// Writes empty line in console.
/// </summary>
/// <param name="numberOfEmptyLines">Number of empty lines which will be written in console. Parameter is optional and default value is 1.</param>
private void WriteEmptyLine(int numberOfEmptyLines = 1)
{
for (int i = 0; i < numberOfEmptyLines; i++)
{
Console.WriteLine();
}
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Pizza
{
internal class Program
{
static void Main(string[] args)
{
var pizzaGame = new PizzaGame();
pizzaGame.Play();
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Text;
namespace Pizza
{
internal static class StringBuilderExtensions
{
/// <summary>
/// Extensions for adding new lines of specific value.
/// </summary>
/// <param name="stringBuilder">Extended class.</param>
/// <param name="value">Value which will be repeated.</param>
/// <param name="numberOfLines">Number of lines that will be appended.</param>
public static void AppendLine(this StringBuilder stringBuilder, string value, int numberOfLines)
{
for (int i = 0; i < numberOfLines; i++)
{
stringBuilder.AppendLine(value);
}
}
}
}

851
71_Poker/java/Poker.java Normal file
View File

@@ -0,0 +1,851 @@
import java.util.Random;
import java.util.Scanner;
import static java.lang.System.out;
/**
* Port of CREATIVE COMPUTING Poker written in Commodore 64 Basic to plain Java
*
* Original source scanned from magazine: https://www.atariarchives.org/basicgames/showpage.php?page=129
*
* I based my port on the OCR'ed source code here: https://github.com/coding-horror/basic-computer-games/blob/main/71_Poker/poker.bas
*
* Why? Because I remember typing this into my C64 when I was a tiny little developer and having great fun playing it!
*
* Goal: Keep the algorithms and UX more or less as-is; Improve the control flow a bit (no goto in Java!) and rename some stuff to be easier to follow.
*
* Result: There are probably bugs, please let me know.
*/
public class Poker {
public static void main(String[] args) {
new Poker().run();
}
float[] cards = new float[50]; // Index 1-5 = Human hand, index 6-10 = Computer hand
float[] B = new float[15];
float playerValuables = 1;
float computerMoney = 200;
float humanMoney = 200;
float pot = 0;
String J$ = "";
float computerHandValue = 0;
int K = 0;
float G = 0;
float T = 0;
int M = 0;
int D = 0;
int U = 0;
float N = 1;
float I = 0;
float X = 0;
int Z = 0;
String handDescription = "";
float V;
void run() {
printWelcome();
playRound();
startAgain();
}
void printWelcome() {
tab(33);
out.println("POKER");
tab(15);
out.print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
out.println();
out.println();
out.println();
out.println("WELCOME TO THE CASINO. WE EACH HAVE $200.");
out.println("I WILL OPEN THE BETTING BEFORE THE DRAW; YOU OPEN AFTER.");
out.println("TO FOLD BET 0; TO CHECK BET .5.");
out.println("ENOUGH TALK -- LET'S GET DOWN TO BUSINESS.");
out.println();
}
void tab(int number) {
System.out.print("\t".repeat(number));
}
int random0to10() {
return new Random().nextInt(10);
}
int removeHundreds(long x) {
return _int(x - (100F * _int(x / 100F)));
}
void startAgain() {
pot = 0;
playRound();
}
void playRound() {
if (computerMoney <= 5) {
computerBroke();
}
out.println("THE ANTE IS $5. I WILL DEAL:");
out.println();
if (humanMoney <= 5) {
playerBroke();
}
pot = pot + 10;
humanMoney = humanMoney - 5;
computerMoney = computerMoney - 5;
for (int Z = 1; Z < 10; Z++) {
generateCards(Z);
}
out.println("YOUR HAND:");
N = 1;
showHand();
N = 6;
I = 2;
describeHand();
out.println();
if (I != 6) {
if (U >= 13) {
if (U <= 16) {
Z = 35;
} else {
Z = 2;
if (random0to10() < 1) {
Z = 35;
}
}
computerOpens();
playerMoves();
} else if (random0to10() >= 2) {
computerChecks();
} else {
I = 7;
Z = 23;
computerOpens();
playerMoves();
}
} else if (random0to10() <= 7) {
if (random0to10() <= 7) {
if (random0to10() >= 1) {
Z = 1;
K = 0;
out.print("I CHECK. ");
playerMoves();
} else {
X = 11111;
I = 7;
Z = 23;
computerOpens();
playerMoves();
}
} else {
X = 11110;
I = 7;
Z = 23;
computerOpens();
playerMoves();
}
} else {
X = 11100;
I = 7;
Z = 23;
computerOpens();
playerMoves();
}
}
void playerMoves() {
playersTurn();
checkWinnerAfterFirstBet();
promptPlayerDrawCards();
}
void computerOpens() {
V = Z + random0to10();
computerMoves();
out.print("I'LL OPEN WITH $" + V);
K = _int(V);
}
@SuppressWarnings("StatementWithEmptyBody")
void computerMoves() {
if (computerMoney - G - V >= 0) {
} else if (G != 0) {
if (computerMoney - G >= 0) {
computerSees();
} else {
computerBroke();
}
} else {
V = computerMoney;
}
}
void promptPlayerDrawCards() {
out.println();
out.println("NOW WE DRAW -- HOW MANY CARDS DO YOU WANT");
inputPlayerDrawCards();
}
void inputPlayerDrawCards() {
T = Integer.parseInt(readString());
if (T == 0) {
computerDrawing();
} else {
Z = 10;
if (T < 4) {
playerDrawsCards();
} else {
out.println("YOU CAN'T DRAW MORE THAN THREE CARDS.");
inputPlayerDrawCards();
}
}
}
// line # 980
void computerDrawing() {
Z = _int(10 + T);
for (U = 6; U <= 10; U++) {
if (_int((float) (X / Math.pow(10F, (U - 6F)))) == (10 * (_int((float) (X / Math.pow(10, (U - 5))))))) {
drawNextCard();
}
}
out.print("I AM TAKING " + _int(Z - 10 - T) + " CARD");
if (Z == 11 + T) {
out.println();
} else {
out.println("S");
}
N = 6;
V = I;
I = 1;
describeHand();
startPlayerBettingAndReaction();
}
void drawNextCard() {
Z = Z + 1;
drawCard();
}
@SuppressWarnings("StatementWithEmptyBody")
void drawCard() {
cards[Z] = 100 * new Random().nextInt(4) + new Random().nextInt(100);
if (_int(cards[Z] / 100) > 3) {
drawCard();
} else if (cards[Z] - 100 * _int(cards[Z] / 100) > 12) {
drawCard();
} else if (Z == 1) {
} else {
for (K = 1; K <= Z - 1; K++) {
if (cards[Z] == cards[K]) {
drawCard();
}
}
if (Z <= 10) {
} else {
N = cards[U];
cards[U] = cards[Z];
cards[Z] = N;
}
}
}
void playerDrawsCards() {
out.println("WHAT ARE THEIR NUMBERS:");
for (int Q = 1; Q <= T; Q++) {
U = Integer.parseInt(readString());
drawNextCard();
}
out.println("YOUR NEW HAND:");
N = 1;
showHand();
computerDrawing();
}
void startPlayerBettingAndReaction() {
computerHandValue = U;
M = D;
if (V != 7) {
if (I != 6) {
if (U >= 13) {
if (U >= 16) {
Z = 2;
playerBetsAndComputerReacts();
} else {
Z = 19;
if (random0to10() == 8) {
Z = 11;
}
playerBetsAndComputerReacts();
}
} else {
Z = 2;
if (random0to10() == 6) {
Z = 19;
}
playerBetsAndComputerReacts();
}
} else {
Z = 1;
playerBetsAndComputerReacts();
}
} else {
Z = 28;
playerBetsAndComputerReacts();
}
}
void playerBetsAndComputerReacts() {
K = 0;
playersTurn();
if (T != .5) {
checkWinnerAfterFirstBetAndCompareHands();
} else if (V == 7 || I != 6) {
computerOpens();
promptAndInputPlayerBet();
checkWinnerAfterFirstBetAndCompareHands();
} else {
out.println("I'LL CHECK");
compareHands();
}
}
void checkWinnerAfterFirstBetAndCompareHands() {
checkWinnerAfterFirstBet();
compareHands();
}
void compareHands() {
out.println("NOW WE COMPARE HANDS:");
J$ = handDescription;
out.println("MY HAND:");
N = 6;
showHand();
N = 1;
describeHand();
out.print("YOU HAVE ");
K = D;
printHandDescriptionResult();
handDescription = J$;
K = M;
out.print(" AND I HAVE ");
printHandDescriptionResult();
out.print(". ");
if (computerHandValue > U) {
computerWins();
} else if (U > computerHandValue) {
humanWins();
} else if (handDescription.contains("A FLUS")) {
someoneWinsWithFlush();
} else if (removeHundreds(M) < removeHundreds(D)) {
humanWins();
} else if (removeHundreds(M) > removeHundreds(D)) {
computerWins();
} else {
handIsDrawn();
}
}
void printHandDescriptionResult() {
out.print(handDescription);
if (!handDescription.contains("A FLUS")) {
K = removeHundreds(K);
printCardValue();
if (handDescription.contains("SCHMAL")) {
out.print(" HIGH");
} else if (!handDescription.contains("STRAIG")) {
out.print("'S");
} else {
out.print(" HIGH");
}
} else {
K = K / 100;
printCardColor();
out.println();
}
}
void handIsDrawn() {
out.print("THE HAND IS DRAWN.");
out.print("ALL $" + pot + " REMAINS IN THE POT.");
playRound();
}
void someoneWinsWithFlush() {
if (removeHundreds(M) > removeHundreds(D)) {
computerWins();
} else if (removeHundreds(D) > removeHundreds(M)) {
humanWins();
} else {
handIsDrawn();
}
}
@SuppressWarnings("StatementWithEmptyBody")
void checkWinnerAfterFirstBet() {
if (I != 3) {
if (I != 4) {
} else {
humanWins();
}
} else {
out.println();
computerWins();
}
}
void computerWins() {
out.print(". I WIN. ");
computerMoney = computerMoney + pot;
potStatusAndNextRoundPrompt();
}
void potStatusAndNextRoundPrompt() {
out.println("NOW I HAVE $" + computerMoney + " AND YOU HAVE $" + humanMoney);
out.print("DO YOU WISH TO CONTINUE");
if (yesFromPrompt()) {
startAgain();
} else {
System.exit(0);
}
}
private boolean yesFromPrompt() {
String h = readString();
if (h != null) {
if (h.toLowerCase().matches("y|yes|yep|affirmative|yay")) {
return true;
} else if (h.toLowerCase().matches("n|no|nope|fuck off|nay")) {
return false;
}
}
out.println("ANSWER YES OR NO, PLEASE.");
return yesFromPrompt();
}
void computerChecks() {
Z = 0;
K = 0;
out.print("I CHECK. ");
playerMoves();
}
void humanWins() {
out.println("YOU WIN.");
humanMoney = humanMoney + pot;
potStatusAndNextRoundPrompt();
}
// line # 1740
void generateCards(int Z) {
cards[Z] = (100 * new Random().nextInt(4)) + new Random().nextInt(100);
if (_int(cards[Z] / 100) > 3) {
generateCards(Z);
return;
}
if (cards[Z] - 100 * (_int(cards[Z] / 100)) > 12) {
generateCards(Z);
return;
}
if (Z == 1) {return;}
for (int K = 1; K <= Z - 1; K++) {// TO Z-1
if (cards[Z] == cards[K]) {
generateCards(Z);
return;
}
}
if (Z <= 10) {return;}
float N = cards[U];
cards[U] = cards[Z];
cards[Z] = N;
}
// line # 1850
void showHand() {
for (int cardNumber = _int(N); cardNumber <= N + 4; cardNumber++) {
out.print(cardNumber + "-- ");
printCardValueAtIndex(cardNumber);
out.print(" OF");
printCardColorAtIndex(cardNumber);
if (cardNumber / 2 == (cardNumber / 2)) {
out.println();
}
}
}
// line # 1950
void printCardValueAtIndex(int Z) {
K = removeHundreds(_int(cards[Z]));
printCardValue();
}
void printCardValue() {
if (K == 9) {
out.print("JACK");
} else if (K == 10) {
out.print("QUEEN");
} else if (K == 11) {
out.print("KING");
} else if (K == 12) {
out.print("ACE");
} else if (K < 9) {
out.print(K + 2);
}
}
// line # 2070
void printCardColorAtIndex(int Z) {
K = _int(cards[Z] / 100);
printCardColor();
}
void printCardColor() {
if (K == 0) {
out.print(" CLUBS");
} else if (K == 1) {
out.print(" DIAMONDS");
} else if (K == 2) {
out.print(" HEARTS");
} else if (K == 3) {
out.print(" SPADES");
}
}
// line # 2170
void describeHand() {
U = 0;
for (Z = _int(N); Z <= N + 4; Z++) {
B[Z] = removeHundreds(_int(cards[Z]));
if (Z == N + 4) {continue;}
if (_int(cards[Z] / 100) != _int(cards[Z + 1] / 100)) {continue;}
U = U + 1;
}
if (U != 4) {
for (Z = _int(N); Z <= N + 3; Z++) {
for (K = Z + 1; K <= N + 4; K++) {
if (B[Z] <= B[K]) {continue;}
X = cards[Z];
cards[Z] = cards[K];
B[Z] = B[K];
cards[K] = X;
B[K] = cards[K] - 100 * _int(cards[K] / 100);
}
}
X = 0;
for (Z = _int(N); Z <= N + 3; Z++) {
if (B[Z] != B[Z + 1]) {continue;}
X = (float) (X + 11 * Math.pow(10, (Z - N)));
D = _int(cards[Z]);
if (U >= 11) {
if (U != 11) {
if (U > 12) {
if (B[Z] != B[Z - 1]) {
fullHouse();
} else {
U = 17;
handDescription = "FOUR ";
}
} else {
fullHouse();
}
} else if (B[Z] != B[Z - 1]) {
handDescription = "TWO PAIR, ";
U = 12;
} else {
handDescription = "THREE ";
U = 13;
}
} else {
U = 11;
handDescription = "A PAIR OF ";
}
}
if (X != 0) {
schmaltzHand();
} else {
if (B[_int(N)] + 3 == B[_int(N + 3)]) {
X = 1111;
U = 10;
}
if (B[_int(N + 1)] + 3 != B[_int(N + 4)]) {
schmaltzHand();
} else if (U != 10) {
U = 10;
X = 11110;
schmaltzHand();
} else {
U = 14;
handDescription = "STRAIGHT";
X = 11111;
D = _int(cards[_int(N + 4)]);
}
}
} else {
X = 11111;
D = _int(cards[_int(N)]);
handDescription = "A FLUSH IN";
U = 15;
}
}
void schmaltzHand() {
if (U >= 10) {
if (U != 10) {
if (U > 12) {return;}
if (removeHundreds(D) <= 6) {
I = 6;
}
} else {
if (I == 1) {
I = 6;
}
}
} else {
D = _int(cards[_int(N + 4)]);
handDescription = "SCHMALTZ, ";
U = 9;
X = 11000;
I = 6;
}
}
void fullHouse() {
U = 16;
handDescription = "FULL HOUSE, ";
}
void playersTurn() {
G = 0;
promptAndInputPlayerBet();
}
String readString() {
Scanner sc = new Scanner(System.in);
return sc.nextLine();
}
@SuppressWarnings("StatementWithEmptyBody")
void promptAndInputPlayerBet() {
out.println("WHAT IS YOUR BET");
T = readFloat();
if (T - _int(T) == 0) {
processPlayerBet();
} else if (K != 0) {
playerBetInvalidAmount();
} else if (G != 0) {
playerBetInvalidAmount();
} else if (T == .5) {
} else {
playerBetInvalidAmount();
}
}
private float readFloat() {
try {
return Float.parseFloat(readString());
} catch (Exception ex) {
System.out.println("INVALID INPUT, PLEASE TYPE A FLOAT. ");
return readFloat();
}
}
void playerBetInvalidAmount() {
out.println("NO SMALL CHANGE, PLEASE.");
promptAndInputPlayerBet();
}
void processPlayerBet() {
if (humanMoney - G - T >= 0) {
humanCanAffordBet();
} else {
playerBroke();
promptAndInputPlayerBet();
}
}
void humanCanAffordBet() {
if (T != 0) {
if (G + T >= K) {
processComputerMove();
} else {
out.println("IF YOU CAN'T SEE MY BET, THEN FOLD.");
promptAndInputPlayerBet();
}
} else {
I = 3;
moveMoneyToPot();
}
}
void processComputerMove() {
G = G + T;
if (G == K) {
moveMoneyToPot();
} else if (Z != 1) {
if (G > 3 * Z) {
computerRaisesOrSees();
} else {
computerRaises();
}
} else if (G > 5) {
if (T <= 25) {
computerRaisesOrSees();
} else {
computerFolds();
}
} else {
V = 5;
if (G > 3 * Z) {
computerRaisesOrSees();
} else {
computerRaises();
}
}
}
void computerRaises() {
V = G - K + random0to10();
computerMoves();
out.println("I'LL SEE YOU, AND RAISE YOU" + V);
K = _int(G + V);
promptAndInputPlayerBet();
}
void computerFolds() {
I = 4;
out.println("I FOLD.");
}
void computerRaisesOrSees() {
if (Z == 2) {
computerRaises();
} else {
computerSees();
}
}
void computerSees() {
out.println("I'LL SEE YOU.");
K = _int(G);
moveMoneyToPot();
}
void moveMoneyToPot() {
humanMoney = humanMoney - G;
computerMoney = computerMoney - K;
pot = pot + G + K;
}
void computerBusted() {
out.println("I'M BUSTED. CONGRATULATIONS!");
System.exit(0);
}
@SuppressWarnings("StatementWithEmptyBody")
private void computerBroke() {
if ((playerValuables / 2) == _int(playerValuables / 2) && playerBuyBackWatch()) {
} else if (playerValuables / 3 == _int(playerValuables / 3) && playerBuyBackTieRack()) {
} else {
computerBusted();
}
}
private int _int(float v) {
return (int) Math.floor(v);
}
private boolean playerBuyBackWatch() {
out.println("WOULD YOU LIKE TO BUY BACK YOUR WATCH FOR $50");
if (yesFromPrompt()) {
computerMoney = computerMoney + 50;
playerValuables = playerValuables / 2;
return true;
} else {
return false;
}
}
private boolean playerBuyBackTieRack() {
out.println("WOULD YOU LIKE TO BUY BACK YOUR TIE TACK FOR $50");
if (yesFromPrompt()) {
computerMoney = computerMoney + 50;
playerValuables = playerValuables / 3;
return true;
} else {
return false;
}
}
// line # 3830
@SuppressWarnings("StatementWithEmptyBody")
void playerBroke() {
out.println("YOU CAN'T BET WITH WHAT YOU HAVEN'T GOT.");
if (playerValuables / 2 != _int(playerValuables / 2) && playerSellWatch()) {
} else if (playerValuables / 3 != _int(playerValuables / 3) && playerSellTieTack()) {
} else {
playerBusted();
}
}
private void playerBusted() {
out.println("YOUR WAD IS SHOT. SO LONG, SUCKER!");
System.exit(0);
}
private boolean playerSellWatch() {
out.println("WOULD YOU LIKE TO SELL YOUR WATCH");
if (yesFromPrompt()) {
if (random0to10() < 7) {
out.println("I'LL GIVE YOU $75 FOR IT.");
humanMoney = humanMoney + 75;
} else {
out.println("THAT'S A PRETTY CRUMMY WATCH - I'LL GIVE YOU $25.");
humanMoney = humanMoney + 25;
}
playerValuables = playerValuables * 2;
return true;
} else {
return false;
}
}
private boolean playerSellTieTack() {
out.println("WILL YOU PART WITH THAT DIAMOND TIE TACK");
if (yesFromPrompt()) {
if (random0to10() < 6) {
out.println("YOU ARE NOW $100 RICHER.");
humanMoney = humanMoney + 100;
} else {
out.println("IT'S PASTE. $25.");
humanMoney = humanMoney + 25;
}
playerValuables = playerValuables * 3;
return true;
} else {
return false;
}
}
}

209
72_Queen/perl/queen.pl Normal file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use constant TARGET => 158;
main(@ARGV);
sub main (@args) {
welcome();
help() if ask_yes_no('DO YOU WANT INSTRUCTIONS');
do { one_match() } while ask_yes_no('ANYONE ELSE CARE TO TRY');
__exit();
}
sub one_match {
print_board();
# the player can choose the starting position in the top row or the
# right column
my $move = ask_first_move() or return forfeit();
# we alternate moves between computer or player from now on
while ('playing') {
$move = computer_move($move);
say "COMPUTER MOVES TO SQUARE $move";
return print_computer_victory() if $move == TARGET;
$move = ask_player_move($move) or return forfeit();
return print_player_victory() if $move == TARGET;
}
}
sub is_valid_move ($move, $current, $skip_prevalidation = 0) {
# pre-validation is needed for moves coming from the user
if (! $skip_prevalidation) {
state $valid_position = { map { $_ => 1 } board_identifiers() };
return 0 unless $move =~ m{\A [1-9]\d+ \z}mxs;
return 1 if $move == 0;
return 0 unless $valid_position->{$move};
return 0 if $move <= $current;
}
# the move might be valid in general, let's check from $current
my $delta = $move - $current;
# a valid move differs from the current position by a multiple of 10,
# or 11, or 21. If dividing by all of them yields a remainder, then
# the move is not valid
return 0 if $delta % 10 && $delta % 11 && $delta % 21;
# otherwise it is
return 1;
}
sub ask_player_move ($current) {
while ('necessary') {
my $move = ask_input('WHAT IS YOUR MOVE');
return $move if is_valid_move($move, $current);
say "\nY O U C H E A T . . . TRY AGAIN";
}
}
sub computer_move ($current) {
# this game has some optimal/safe positions from where it's possible
# to win with the right strategy. We will aim for them, if possible
state $optimals = [ 158, 127, 126, 75, 73 ];
for my $optimal ($optimals->@*) {
# moves can only increase, if we did not find any optimal move so far
# then there's no point going on
last if $optimal <= $current;
# computer moves are "syntactically" valid, skip pre-validation
return $optimal if is_valid_move($optimal, $current, 'skip');
}
# cannot reach an optimal position... resort to randomness
my $z = rand();
return $current + 11 if $z > 0.6; # move down
return $current + 21 if $z > 0.3; # move diagonally
return $current + 10; ; # move horizontally
}
sub board_identifiers {
return (
81, 71, 61, 51, 41, 31, 21, 11,
92, 82, 72, 62, 52, 42, 32, 22,
103, 93, 83, 73, 63, 53, 43, 33,
114, 104, 94, 84, 74, 64, 54, 44,
125, 115, 105, 95, 85, 75, 65, 55,
136, 126, 116, 106, 96, 86, 76, 66,
147, 137, 127, 117, 107, 97, 87, 77,
158, 148, 138, 128, 118, 108, 98, 88,
);
}
sub print_player_victory {
print <<'END';
C O N G R A T U L A T I O N S . . .
YOU HAVE WON--VERY WELL PLAYED.
IT LOOKS LIKE I HAVE MET MY MATCH.
THANKS FOR PLAYING---I CAN'T WIN ALL THE TIME.
END
}
sub print_computer_victory {
print <<'END';
NICE TRY, BUT IT LOOKS LIKE I HAVE WON.
THANKS FOR PLAYING.
END
}
sub forfeit { say "\nIT LOOKS LIKE I HAVE WON BY FORFEIT.\n" }
sub ask_input ($prompt) {
print "$prompt? ";
defined(my $input = <STDIN>) or __exit();
# remove spaces from the input (including newlines), they are not used
$input =~ s{\s+}{}gmxs;
return $input;
}
sub ask_yes_no ($prompt) {
while ('necessary') {
my $input = ask_input($prompt);
return 1 if $input =~ m{\A (?: yes | y) \z}imxs;
return 0 if $input =~ m{\A (?: no | n) \z}imxs;
say q{PLEASE ANSWER 'YES' OR 'NO'.};
}
}
sub ask_first_move {
while ('necessary') {
my $input = ask_input('WHERE WOULD YOU LIKE TO START');
if ($input =~ m{\A (?: 0 | [1-9]\d+) \z}mxs) {
return 0 unless $input;
my $diagonal = int($input / 10);
my $row = $input % 10;
return $input if $row == 1 || $row == $diagonal;
}
say <<'END'
PLEASE READ THE DIRECTIONS AGAIN.
YOU HAVE BEGUN ILLEGALLY.
END
}
}
sub __exit {
say "\nOK --- THANKS AGAIN.";
exit 0;
}
sub welcome {
print <<'END'
QUEEN
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
END
}
sub help {
print <<'END';
WE ARE GOING TO PLAY A GAME BASED ON ONE OF THE CHESS
MOVES. OUR QUEEN WILL BE ABLE TO MOVE ONLY TO THE LEFT,
DOWN, OR DIAGONALLY DOWN AND TO THE LEFT.
THE OBJECT OF THE GAME IS TO PLACE THE QUEEN IN THE LOWER
LEFT HAND SQUARE BY ALTERNATING MOVES BETWEEN YOU AND THE
COMPUTER. THE FIRST ONE TO PLACE THE QUEEN THERE WINS.
YOU GO FIRST AND PLACE THE QUEEN IN ANY ONE OF THE SQUARES
ON THE TOP ROW OR RIGHT HAND COLUMN.
THAT WILL BE YOUR FIRST MOVE.
WE ALTERNATE MOVES.
YOU MAY FORFEIT BY TYPING '0' AS YOUR MOVE.
BE SURE TO PRESS THE RETURN KEY AFTER EACH RESPONSE.
END
}
sub print_board {
say '';
my @ids = board_identifiers();
my $row_template = join ' ', ($ENV{ORIGINAL} ? '%d' : '%3d') x 8;
for my $A (0 .. 7) {
my $start = $A * 8;
my @range = $start .. $start + 7;
say ' ', sprintf $row_template, @ids[@range];
say "\n";
}
say '';
}

111
73_Reverse/ruby/reverse.rb Normal file
View File

@@ -0,0 +1,111 @@
ARRAYSIZE = 9
$digitArray = Array.new(ARRAYSIZE)
$winningArray = Array.new(ARRAYSIZE)
# Method to print the rules
def displayTheRules
puts "This is the game of 'Reverse'. to win, all you have"
puts "to do is arrange a list of numbers (1 through " + ARRAYSIZE.to_s + ")"
puts "in numerical order from left to right. to move, you"
puts "tell me how many numbers (counting from the left) to"
puts "reverse. For example, if the current list is:"
puts "2 3 4 5 1 6 7 8 9"
puts "and you reverse 4, the result will be:"
puts "5 4 3 2 1 6 7 8 9"
puts "Now if you reverse 5, you win!"
puts "1 2 3 4 5 6 7 8 9"
puts "No doubt you will like this game, but"
puts "if you want to quit, reverse 0 (zero)."
end
# Method to print the list
def printList
puts "\n" + $digitArray.join(" ") + "\n\n"
end
# Zero-based arrays contain digits 1-9
# Make a random array and an ordered winning answer array A[0] to A[N]
def makeRandomList
for kIndex in 0..ARRAYSIZE-1 do
$digitArray[kIndex] = kIndex+1
$winningArray[kIndex] = kIndex+1
end
# now randomize the digit array order
$digitArray.shuffle!
end
def checkForWin? (triesSoFar)
# Check for a win (all array elements in order)
if $digitArray == $winningArray then
puts "You won it in " + triesSoFar.to_s + " moves!!!\n\n"
puts "try again (yes or no)?"
tryAgain = gets.strip.upcase
if tryAgain == "YES" then
return true
end
puts "\nO.K. Hope you had fun!!"
exit
end
return false
end
def reverseIt (howManyToReverse, triesSoFar)
# REVERSE R NUMBERS AND PRINT NEW LIST
# extract and reverse the first howManyToReverse elements of the array
subArray = $digitArray.take(howManyToReverse)
subArray.reverse!
# get the remaining elements of the original array
endArray = $digitArray.slice(howManyToReverse, ARRAYSIZE)
# append those elements to the reversed elements
$digitArray = subArray.concat(endArray)
# if we got all in order, randomize again
isWinner = checkForWin?(triesSoFar)
if isWinner == true then
makeRandomList
end
printList # always print the newly ordered list
return isWinner
end
def askHowManyToReverse
puts "How many shall I reverse?";
rNumber = gets.to_i
if rNumber > 0 then
if rNumber > ARRAYSIZE then
puts "Oops! Too many! I can reverse at most " + ARRAYSIZE.to_s
end
else
rNumber = 0 # zero or negative values end the game
end
return rNumber
end
puts "REVERSE"
puts "Creative Computing Morristown, New Jersey\n\n\n"
puts "REVERSE -- A game of skill\n\n"
puts "Do you want the rules?"
wantRules = gets.strip.upcase
if wantRules == "YES" then
displayTheRules
end
makeRandomList
howManyTries = 0
puts "\nHere we go ... the list is:"
printList # display the initial list
# start the game loop
r = askHowManyToReverse
while r != 0 do # zero will end the game
if r <= ARRAYSIZE then
howManyTries = howManyTries+1
if reverseIt(r, howManyTries) then
howManyTries = 0
end
end
r = askHowManyToReverse
end

View File

@@ -1,3 +1,10 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Oracle Java](https://openjdk.java.net/)
Two versions of Roulette has been contributed. They are indicated within given sub-folders
- [oop](./oop) - Conversion by Andrew McGuinness (andrew@arobeia.co.uk)
- [iterative](./iterative) - Conversion by Thomas Kwashnak ([Github](https://github.com/LittleTealeaf)).
- Implements features from JDK 17.
- Does make use of some object oriented programming, but acts as a more iterative solution.

View File

@@ -0,0 +1,277 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
public class Roulette {
private static Set<Integer> RED_NUMBERS;
static {
RED_NUMBERS = Set.of(1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36);
}
private PrintStream out;
private Scanner scanner;
private int houseBalance, playerBalance;
private Random random;
public Roulette(PrintStream out, InputStream in) {
this.out = out;
this.scanner = new Scanner(in);
houseBalance = 100000;
playerBalance = 1000;
random = new Random();
}
public static void main(String[] args) {
new Roulette(System.out, System.in).play();
}
public void play() {
out.println(" ROULETTE");
out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
out.println("WELCOME TO THE ROULETTE TABLE\n");
out.print("DO YOU WANT INSTRUCTIONS? ");
if (scanner.nextLine().toLowerCase().charAt(0) != 'n') {
printInstructions();
}
do {
Bet[] bets = queryBets();
out.print("SPINNING...\n\n");
int result = random.nextInt(1, 39);
/*
Equivalent to following line
if(result == 37) {
out.println("00");
} else if(result == 38) {
out.println("0");
} else if(RED_NUMBERS.contains(result)) {
out.println(result + " RED");
} else {
out.println(result + " BLACK");
}
*/
out.println(switch (result) {
case 37 -> "00";
case 38 -> "0";
default -> result + (RED_NUMBERS.contains(result) ? " RED" : " BLACK");
});
betResults(bets, result);
out.println();
out.println("TOTALS:\tME\t\tYOU");
out.format("\t\t%5d\t%d\n", houseBalance, playerBalance);
} while (playAgain());
if (playerBalance <= 0) {
out.println("THANKS FOR YOUR MONEY\nI'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL");
} else {
printCheck();
}
out.println("COME BACK SOON!");
}
public void printInstructions() {
out.println();
out.println("THIS IS THE BETTING LAYOUT");
out.println(" (*=RED)");
out.println();
out.println(" 1* 2 3*");
out.println(" 4 5* 6 ");
out.println(" 7* 8 9*");
out.println("10 11 12*");
out.println("---------------");
out.println("13 14* 15 ");
out.println("16* 17 18*");
out.println("19* 20 21*");
out.println("22 23* 24 ");
out.println("---------------");
out.println("25* 26 27*");
out.println("28 29 30*");
out.println("31 32* 33 ");
out.println("34* 35 36*");
out.println("---------------");
out.println(" 00 0 ");
out.println();
out.println("TYPES OF BETS");
out.println();
out.println("THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET");
out.println("ON THAT NUMBER.");
out.println("THESE PAY OFF 35:1");
out.println();
out.println("THE 2:1 BETS ARE:");
out.println(" 37) 1-12 40) FIRST COLUMN");
out.println(" 38) 13-24 41) SECOND COLUMN");
out.println(" 39) 25-36 42) THIRD COLUMN");
out.println();
out.println("THE EVEN MONEY BETS ARE:");
out.println(" 43) 1-18 46) ODD");
out.println(" 44) 19-36 47) RED");
out.println(" 45) EVEN 48) BLACK");
out.println();
out.println(" 49)0 AND 50)00 PAY OFF 35:1");
out.println(" NOTE: 0 AND 00 DO NOT COUNT UNDER ANY");
out.println(" BETS EXCEPT THEIR OWN.");
out.println();
out.println("WHEN I ASK FOR EACH BET, TYPE THE NUMBER");
out.println("AND THE AMOUNT, SEPARATED BY A COMMA.");
out.println("FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500");
out.println("WHEN I ASK FOR A BET.");
out.println();
out.println("THE MINIMUM BET IS $5, THE MAXIMUM IS $500.");
}
private Bet[] queryBets() {
int numBets = -1;
while (numBets < 1) {
out.print("HOW MANY BETS? ");
try {
numBets = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException ignored) {
}
}
Bet[] bets = new Bet[numBets];
for (int i = 0; i < numBets; i++) {
while (bets[i] == null) {
try {
out.print("NUMBER" + (i + 1) + "? ");
String[] values = scanner.nextLine().split(",");
int betNumber = Integer.parseInt(values[0]);
int betValue = Integer.parseInt(values[1]);
for (int j = 0; j < i; j++) {
if (bets[j].num == betNumber) {
out.println("YOU MADE THAT BET ONCE ALREADY,DUM-DUM");
betNumber = -1; //Since -1 is out of the range, this will throw it out at the end
}
}
if (betNumber > 0 && betNumber <= 50 && betValue >= 5 && betValue <= 500) {
bets[i] = new Bet(betNumber,betValue);
}
} catch (Exception ignored) {
}
}
}
return bets;
}
private void betResults(Bet[] bets, int num) {
for (int i = 0; i < bets.length; i++) {
Bet bet = bets[i];
/*
Using a switch statement of ternary operators that check if a certain condition is met based on the bet value
Returns the coefficient that the bet amount should be multiplied by to get the resulting value
*/
int coefficient = switch (bet.num) {
case 37 -> (num <= 12) ? 2 : -1;
case 38 -> (num > 12 && num <= 24) ? 2 : -1;
case 39 -> (num > 24 && num < 37) ? 2 : -1;
case 40 -> (num < 37 && num % 3 == 1) ? 2 : -1;
case 41 -> (num < 37 && num % 3 == 2) ? 2 : -1;
case 42 -> (num < 37 && num % 3 == 0) ? 2 : -1;
case 43 -> (num <= 18) ? 1 : -1;
case 44 -> (num > 18 && num <= 36) ? 1 : -1;
case 45 -> (num % 2 == 0) ? 1 : -1;
case 46 -> (num % 2 == 1) ? 1 : -1;
case 47 -> RED_NUMBERS.contains(num) ? 1 : -1;
case 48 -> !RED_NUMBERS.contains(num) ? 1 : -1;
case 49 -> (num == 37) ? 35 : -1;
case 50 -> (num == 38) ? 35 : -1;
default -> (bet.num < 49 && bet.num == num) ? 35 : -1;
};
int betResult = bet.amount * coefficient;
if (betResult < 0) {
out.println("YOU LOSE " + -betResult + " DOLLARS ON BET " + (i + 1));
} else {
out.println("YOU WIN " + betResult + " DOLLARS ON BET " + (i + 1));
}
playerBalance += betResult;
houseBalance -= betResult;
}
}
private boolean playAgain() {
if (playerBalance <= 0) {
out.println("OOPS! YOU JUST SPENT YOUR LAST DOLLAR!");
return false;
} else if (houseBalance <= 0) {
out.println("YOU BROKE THE HOUSE!");
playerBalance = 101000;
houseBalance = 0;
return false;
} else {
out.println("PLAY AGAIN?");
return scanner.nextLine().toLowerCase().charAt(0) == 'y';
}
}
private void printCheck() {
out.print("TO WHOM SHALL I MAKE THE CHECK? ");
String name = scanner.nextLine();
out.println();
for (int i = 0; i < 72; i++) {
out.print("-");
}
out.println();
for (int i = 0; i < 50; i++) {
out.print(" ");
}
out.println("CHECK NO. " + random.nextInt(0, 100));
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
out.println();
out.println("PAY TO THE ORDER OF -----" + name + "----- $" + (playerBalance));
out.println();
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println("THE MEMORY BANK OF NEW YORK");
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println("THE COMPUTER");
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println("----------X-----");
for (int i = 0; i < 72; i++) {
out.print("-");
}
out.println();
}
public class Bet {
final int num, amount;
public Bet(int num, int amount) {
this.num = num;
this.amount = amount;
}
}
}

View File

@@ -0,0 +1,65 @@
/* A bet has a target (the code entered, which is 1-36, or special values for
* the various groups, zero and double-zero), and an amount in dollars
*/
public class Bet {
public int target;
public int amount;
/* bet on a target, of an amount */
public Bet(int on, int of) {
target = on; amount = of;
}
/* check if this is a valid bet - on a real target and of a valid amount */
public boolean isValid() {
return ((target > 0) && (target <= 50) &&
(amount >= 5) && (amount <= 500));
}
/* utility to return either the odds amount in the case of a win, or zero for a loss */
private int m(boolean isWon, int odds) {
return isWon? odds: 0;
}
/* look at the wheel to see if this bet won.
* returns 0 if it didn't, or the odds if it did
*/
public int winsOn(Wheel w) {
if (target < 37) {
// A number bet 1-36 wins at odds of 35 if it is the exact number
return m(w.isNumber() && (w.number() == target), 35);
} else
switch (target) {
case 37: // 1-12, odds of 2
return m(w.isNumber() && (w.number() <= 12), 2);
case 38: // 13-24, odds of 2
return m(w.isNumber() && (w.number() > 12) && (w.number() <= 24), 2);
case 39: // 25-36, odds of 2
return m(w.isNumber() && (w.number() > 24), 2);
case 40: // Column 1, odds of 2
return m(w.isNumber() && ((w.number() % 3) == 1), 2);
case 41: // Column 2, odds of 2
return m(w.isNumber() && ((w.number() % 3) == 2), 2);
case 42: // Column 3, odds of 2
return m(w.isNumber() && ((w.number() % 3) == 0), 2);
case 43: // 1-18, odds of 1
return m(w.isNumber() && (w.number() <= 18), 1);
case 44: // 19-36, odds of 1
return m(w.isNumber() && (w.number() > 18), 1);
case 45: // even, odds of 1
return m(w.isNumber() && ((w.number() %2) == 0), 1);
case 46: // odd, odds of 1
return m(w.isNumber() && ((w.number() %2) == 1), 1);
case 47: // red, odds of 1
return m(w.isNumber() && (w.color() == Wheel.BLACK), 1);
case 48: // black, odds of 1
return m(w.isNumber() && (w.color() == Wheel.RED), 1);
case 49: // single zero, odds of 35
return m(w.value().equals("0"), 35);
case 50: // double zero, odds of 35
return m(w.value().equals("00"), 35);
}
throw new RuntimeException("Program Error - invalid bet");
}
}

View File

@@ -0,0 +1,234 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class Roulette {
public static void main(String args[]) throws Exception {
Roulette r = new Roulette();
r.play();
}
private BufferedReader reader;
private PrintStream writer;
private int house; // how much money does the house have
private int player; // how much money does the player have
private Wheel wheel = new Wheel();
public Roulette() {
reader = new BufferedReader(new InputStreamReader(System.in));
writer = System.out;
house = 100000;
player = 1000;
}
// for a test / cheat mode -- set the random number generator to a known value
private void setSeed(long l) {
wheel.setSeed(l);
}
public void play() {
try {
intro();
writer.println("WELCOME TO THE ROULETTE TABLE\n" +
"DO YOU WANT INSTRUCTIONS");
String instr = reader.readLine();
if (!instr.toUpperCase().startsWith("N"))
instructions();
while (betAndSpin()) { // returns true if the game is to continue
}
if (player <= 0) {
// player ran out of money
writer.println("THANKS FOR YOUR MONEY.\nI'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL");
} else {
// player has money -- print them a check
writer.println("TO WHOM SHALL I MAKE THE CHECK");
String payee = reader.readLine();
writer.println("-".repeat(72));
tab(50); writer.println("CHECK NO. " + (new Random().nextInt(100) + 1));
writer.println();
tab(40); writer.println(LocalDate.now().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
writer.println("\n\nPAY TO THE ORDER OF-----" + payee + "-----$ " + player);
writer.print("\n\n");
tab(10); writer.println("THE MEMORY BANK OF NEW YORK\n");
tab(40); writer.println("THE COMPUTER");
tab(40); writer.println("----------X-----\n");
writer.println("-".repeat(72));
writer.println("COME BACK SOON!\n");
}
}
catch (IOException e) {
// this should not happen
System.err.println("System error:\n" + e);
}
}
/* Write the starting introduction */
private void intro() throws IOException {
tab(32); writer.println("ROULETTE");
tab(15); writer.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n");
}
/* Display the game instructions */
private void instructions() {
String[] instLines = new String[] {
"THIS IS THE BETTING LAYOUT",
" (*=RED)",
"" ,
" 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*",
"---------------",
" 00 0 ",
"" ,
"TYPES OF BETS",
"" ,
"THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET",
"ON THAT NUMBER.",
"THESE PAY OFF 35:1",
"" ,
"THE 2:1 BETS ARE:",
" 37) 1-12 40) FIRST COLUMN",
" 38) 13-24 41) SECOND COLUMN",
" 39) 25-36 42) THIRD COLUMN",
"" ,
"THE EVEN MONEY BETS ARE:",
" 43) 1-18 46) ODD",
" 44) 19-36 47) RED",
" 45) EVEN 48) BLACK",
"",
" 49)0 AND 50)00 PAY OFF 35:1",
" NOTE: 0 AND 00 DO NOT COUNT UNDER ANY",
" BETS EXCEPT THEIR OWN.",
"",
"WHEN I ASK FOR EACH BET, TYPE THE NUMBER",
"AND THE AMOUNT, SEPARATED BY A COMMA.",
"FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500",
"WHEN I ASK FOR A BET.",
"",
"THE MINIMUM BET IS $5, THE MAXIMUM IS $500.",
"" };
writer.println(String.join("\n", instLines));
}
/* Take a set of bets from the player, then spin the wheel and work out the winnings *
* This returns true if the game is to continue afterwards
*/
private boolean betAndSpin() throws IOException {
int betCount = 0;
while (betCount == 0) { // keep asking how many bets until we get a good answer
try {
writer.println("HOW MANY BETS");
String howMany = reader.readLine();
betCount = Integer.parseInt(howMany.strip());
if ((betCount < 1) || (betCount > 100)) betCount = 0; // bad -- set zero and ask again
}
catch (NumberFormatException e) {
// this happens if the input is not a number
writer.println("INPUT ERROR");
}
}
HashSet<Integer> betsMade = new HashSet<>(); // Bet targets already made, so we can spot repeats
ArrayList<Bet> bets = new ArrayList<>(); // All the bets for this round
while (bets.size() < betCount) {
Bet bet = new Bet(0, 0); // an invalid bet to hold the place
while (!bet.isValid()) { // keep asking until it is valid
try {
writer.println("NUMBER " + (bets.size() + 1));
String fields[] = reader.readLine().split(",");
if (fields.length == 2) {
bet = new Bet(Integer.parseInt(fields[0].strip()),
Integer.parseInt(fields[1].strip()));
}
}
catch (NumberFormatException e) {
writer.println("INPUT ERROR");
}
}
// Check if there is already a bet on the same target
if (betsMade.contains(bet.target)) {
writer.println("YOU MADE THAT BET ONCE ALREADY,DUM-DUM");
} else {
betsMade.add(bet.target); // note this target has now been bet on
bets.add(bet);
}
}
writer.println("SPINNING\n\n");
wheel.spin(); // this deliberately takes some random amount of time
writer.println(wheel.value());
// go through the bets, and evaluate each one
int betNumber = 1;
for (Bet b : bets) {
int multiplier = b.winsOn(wheel);
if (multiplier == 0) {
// lost the amount of the bet
writer.println("YOU LOSE " + b.amount + " DOLLARS ON BET " + betNumber);
house += b.amount;
player -= b.amount;
} else {
// won the amount of the bet, multiplied by the odds
int winnings = b.amount * multiplier;
writer.println("YOU WIN " + winnings + " DOLLARS ON BET " + betNumber);
house -= winnings;
player += winnings;
}
++betNumber;
}
writer.println("\nTOTALS:\tME\tYOU\n\t" + house + "\t" + player);
if (player <= 0) {
writer.println("OOPS! YOU JUST SPENT YOUR LAST DOLLAR");
return false; // do not repeat since the player has no more money
}
if (house <= 0) {
writer.println("YOU BROKE THE HOUSE!");
player = 101000; // can't win more than the house started with
return false; // do not repeat since the house has no more money
}
// player still has money, and the house still has money, so ask the player
// if they want to continue
writer.println("AGAIN");
String doContinue = reader.readLine();
// repeat if the answer was not "n" or "no"
return (!doContinue.toUpperCase().startsWith("N"));
}
// utility to print n spaces for formatting
private void tab(int n) {
writer.print(" ".repeat(n));
}
}

View File

@@ -0,0 +1,69 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
// The roulette wheel
public class Wheel {
// List the numbers which are black
private HashSet<Integer> black = new HashSet<>(Arrays.asList(new Integer[] { 1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36 }));
private Random random = new Random();
private int pocket = 38;
public static final int ZERO=0;
public static final int BLACK=1;
public static final int RED=2;
// Set up a wheel. You call "spin", and then can check the result.
public Wheel() {
}
// Cheat / test mode
void setSeed(long l) {
random.setSeed(l);
}
// Spin the wheel onto a new random value.
public void spin() {
// keep spinning for a while
do {
try {
// 1 second delay. Where it stops, nobody knows
Thread.sleep(1000);
}
catch (InterruptedException e) {}
pocket = random.nextInt(38) + 1;
} while (random.nextInt(4) > 0); // keep spinning until it stops
}
// The string representation of the number; 1-36, 0, or 00
public String value() {
if (pocket == 37) return "0";
else if (pocket == 38) return "00";
else return String.valueOf(pocket);
}
// True if either 0 or 00 is hit
public boolean zero() {
return (pocket > 36);
}
// True if anything other than 0 or 00 is hit
public boolean isNumber() {
return (pocket < 37);
}
// The number rolled
public int number() {
if (zero()) return 0;
else return pocket;
}
// Either ZERO, BLACK, or RED
public int color() {
if (zero()) return ZERO;
else if (black.contains(pocket)) return BLACK;
else return RED;
}
}

View File

@@ -1,3 +1,15 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
This conversion consists of three files in `75_Roulette/perl/`:
- `roulette.pl` is the port of the BASIC to Perl;
- `roulette-test.t` is a Perl test for correctness of display and payout;
- `make-roulette-test.pl` generates roulette-test.t from roulette.bas.
The ported version of the game numbers the slots from 0 rather than 1, and uses a dispatch table to figure out the payout.
The Perl test loads `roulette.pl` and verifies the Perl slot display and payout logic against the BASIC for all combinations of slots and bets. If any tests fail that fact will be noted at the end of the output.
The test code is generated by reading the BASIC, retaining only the slot display and payout logic (based on line numbers), and wrapping this in code that generates all combinations of bet and spin result. The result is run, and the result is captured and parsed to produce `roulette-test.t`. `make-roulette-test.pl` has some command-line options that may be of interest. `--help` will display the documentation.

View File

@@ -0,0 +1,263 @@
#!/usr/bin/env perl
use 5.014; # For s///r
use strict;
use warnings;
use File::Temp;
use Getopt::Long 2.33 qw{ :config auto_version };
use IPC::Cmd qw{ can_run }; # Core as of Perl 5.9.5.
use Pod::Usage;
our $VERSION = '0.000_01';
my %opt = (
program => find_basic(),
output => make_default_output(),
);
GetOptions( \%opt,
qw{ output=s program=s },
help => sub { pod2usage( { -verbose => 2 } ) },
) or pod2usage( { -verbose => 0 } );
die "No default BASIC found; you must specify --program\n"
unless defined $opt{program};
my $game_dir = ( File::Spec->splitdir( $0 ) )[0];
my $basic_file = File::Spec->catfile( $game_dir, 'roulette.bas' );
open my $basic_handle, '<', $basic_file
or die "Unable to open $basic_file: $!\n";
my $munged = File::Temp->new();
print { $munged } <<'EOD';
1000 Y=50
1010 DIM B(100),C(100),T(100)
1090 FOR S=1 TO 38
1095 PRINT "SPIN ";S
1100 FOR C=1 TO Y
1110 B(C)=1
1120 T(C)=C
1130 NEXT C
EOD
transcribe( $basic_file, $basic_handle, $munged, 1860, 2810 );
transcribe( $basic_file, $basic_handle, $munged, 2950 );
say { $munged } '4000 NEXT S';
$munged->flush();
if ( $opt{output} ne '-' ) {
my $dir = ( File::Spec->splitpath( $0 ) )[1];
my $fn = File::Spec->rel2abs( $opt{output}, $dir );
$fn = File::Spec->abs2rel( $fn );
open my $fh, '>', $fn
or die "Unable to open $fn: $!\n";
warn "Writing $fn\n";
select $fh;
}
print <<'EOD';
package main;
use 5.010;
use strict;
use warnings;
use File::Spec;
use Test::More 0.88; # Because of done_testing();
EOD
print <<"EOD";
# NOTE: This file is generated by $0.
# Any edits made to it will be lost the next time it is regenerated.
# Caveat coder.
EOD
print <<'EOD';
my $dir = ( File::Spec->splitpath( $0 ) )[1];
my $script = File::Spec->catfile( $dir, 'roulette.pl' );
{
# Modern Perls do not have . in @INC, but we need it there to load a
# relative path.
local @INC = ( File::Spec->curdir(), @INC );
require $script; # Load game as module
}
EOD
my $spin;
my $name;
foreach ( `$opt{program} @{[ $munged->filename() ]}` ) {
s/\N{U+1D}/ /smxg; # Artifact of the BASIC I'm using.
s/ \s+ \z //smx;
s/ \A \s+ //smx;
if ( $_ eq '' ) {
# Ignore empty lines.
} elsif ( m/ \A SPIN \s* ( [0-9]+ ) /smx ) {
$spin = $1 - 1; # BASIC is 1-based, but Perl is 0-based
} elsif ( m/ \A YOU \s+ WIN \s* ( [0-9]+ ) \s*
DOLLARS \s+ ON \s+ BET \s* ( [0-9]+ ) /smx ) {
say "is payout( $spin, $2 ), $1, 'Spin $spin ($name), bet $2 pays $1';";
} elsif ( m/ \A YOU \s+ LOSE \s* ( [0-9]+ ) \s*
DOLLARS \s+ ON \s+ BET \s* ( [0-9]+ ) /smx ) {
say "is payout( $spin, $2 ), -$1, 'Spin $spin ($name), bet $2 pays -$1';";
} elsif ( m/ \A \s* ( [0-9]+ ) (?: \s* ( [[:alpha:]]+ ) )? \z /smx ) {
$name = $2 ? sprintf( '%d %s', $1, ucfirst lc $2 ) : $1;
say "is format_spin( $spin ), '$name', 'Spin $spin is $name';";
} else {
die "Unexpected input $_";
}
}
print <<'EOD';
done_testing;
1;
# ex: set textwidth=72 :
EOD
sub find_basic {
# yabasic seems not to work
foreach my $prog ( qw{ basic cbmbasic } ) {
return $prog if can_run( $prog )
}
return undef;
}
sub make_default_output {
( my $rslt = $0 ) =~ s/ [.] pl \z /.t/smx;
$rslt =~ s/ .* \b make- //smx;
return $rslt;
}
sub transcribe {
my ( $in_file, $in_handle, $out_handle, $first_line, $last_line ) = @_;
$last_line //= $first_line;
while ( <$in_handle> ) {
m/ \A \s* ( [0-9]+ )+ \s /smx
or next;
$1 < $first_line
and next;
say { $out_handle } sprintf '%04d REM BEGIN VERBATIM FROM %s',
$first_line - 10, $in_file;
print { $out_handle } $_;
last;
}
while ( <$in_handle> ) {
m/ \A \s* ( [0-9]+ )+ \s /smx
and $1 > $last_line
and last;
print { $out_handle } $_;
}
say { $out_handle } sprintf '%04d REM END VERBATIM FROM %s',
$last_line + 10, $in_file;
return;
}
__END__
=head1 TITLE
make-roulette-test.pl - Generate the tests for 75_Roulette/perl/roulette.pl
=head1 SYNOPSIS
perl 75_Roulette/perl/make-roulette-test.pl
perl 75_Roulette/perl/make-roulette-test.pl --program mybasic
perl 75_Roulette/perl/make-roulette-test.pl --help
perl 75_Roulette/perl/make-roulette-test.pl --version
=head1 OPTIONS
<<< replace boiler plate >>>
=head2 --help
This option displays the documentation for this script. The script then
exits.
=head2 --output
--output fubar.t
This option specifies the output file. This needs to be in the same
directory as F<roulette.pl>, and defaults to that directory. A single
dash (C<'-'>) is special-cased to send the output to standard out.
The default is C<--output=test-roulette.t>.
=head2 --program
--program my_basic
This option specifies the name of your BASIC interpreter. This must be
the name of an executable file in your PATH (aliases do not work).
The default is the first-found in the list C<qw{ basic cbmbasic }>.
=head2 --version
This option displays the version of this script. The script then exits.
=head1 DETAILS
This Perl script generates F<roulette-test.t>, which tests
F<roulette.pl>. The latter is expected to be written as a modulino.
This script assumes that:
=over
=item * it is in the same directory as F<roulette.pl>;
=item * F<roulette.bas> is in the first-level subdirectory under the current directory;
=back
The generated test assumes that it is in the same directory as
F<roulette.pl>.
This script works by abstracting the internals of F<roulette.bas> and
wrapping them in a loop that generates all possible spins, and places
all possible bets on each spin. The generated BASIC is written to a
temporary file, and executed by a BASIC interpreter. The output is
parsed and used to generate the output.
Obviously there is some ad-hocery going on, and this script has only
been tested under C<cbmbasic>, which was what I had on hand.
B<Caveat:> the abstraction process is driven by BASIC line numbers. Any
change of these puts the ad-hocery at risk.
=head1 AUTHOR
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set textwidth=72 :

File diff suppressed because it is too large Load Diff

319
75_Roulette/perl/roulette.pl Executable file
View File

@@ -0,0 +1,319 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use POSIX qw{ strftime }; # Time formatting
use Term::ReadLine; # Prompt and return user input
our $VERSION = '0.000_01';
# A main() function is not usual in Perl scripts. I have installed one
# here to make the script into a "modulino." The next line executes
# main() if and only if caller() returns false. It will do this if we
# were loaded by another Perl script but not otherwise. This was done so
# I could test the payout and spin formatting logic.
main() unless caller;
sub main {
print <<'EOD';
ROULETTE
Creative Computing Morristown, New Jersey
Welcome to the roulette table.
EOD
if ( get_yes_no( 'Do you want instructions' ) ) {
print <<'EOD';
This is the betting layout
(*=red)
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*
---------------
00 0
Types of bets:
The numbers 1 to 36 signify a straight bet
on that number.
These pay off 35:1
The 2:1 bets are:
37) 1-12 40) first column
38) 13-24 41) second column
39) 25-36 42) third column
The even money bets are:
43) 1-18 46) odd
44) 19-36 47) red
45) even 48) black
49) 0 and 50) 00 pay off 35:1
Note: 0 and 00 do not count under any
bets except their own.
When I ask for each bet, type the number
and the amount, separated by a comma.
For example: to bet $500 on black, type 48,500
when I ask for a bet.
The minimum bet is $5, the maximum is $500.
EOD
}
my $P = 1000;
my $D = 100000.;
while ( 1 ) { # Iterate indefinitely
my $Y = get_input( 'How many bets? ',
sub { m/ \A [0-9]+ \z /smx && $ARG > 0 && $ARG <= 50 },
"Please enter a positive integer no greater than 50\n",
);
my @B;
my @T;
foreach my $C ( 1 .. $Y ) {
my ( $X, $Z ) = split qr< , >smx, get_input(
"Number $C: ",
sub { m/ \A ( [0-9]+ ) , ( [0-9]+ ) \z /smx
&& $1 > 0 && $1 <= 50 && $2 >= 5 && $2 <= 500 },
"Please enter two comma-separated positive numbers\n",
);
if ( $B[$X] ) {
say 'You made that bet once already, dum-dum.';
redo;
}
$B[$X] = $Z; # BASIC does $B[$C] = $Z
$T[$C] = $X;
}
print <<'EOD';
Spinning ...
EOD
my $S = int rand 38; # Zero-based, versus 1-based in BASIC
say format_spin( $S );
say '';
foreach my $C ( 1 .. $Y ) {
my $X = $T[$C];
my $payout = payout( $S, $X ) * $B[$X];
$D -= $payout;
$P += $payout;
if ( $payout > 0 ) {
say "You win $payout dollars on bet $C";
} else {
$payout = -$payout;
say "You lose $payout dollars on bet $C";
}
}
say "Totals\tMe\tYou";
say "\t$D\t$P";
say '';
last unless get_yes_no( 'Again' );
}
say '';
if ( $P > 0 ) {
my $B = get_input(
'To whom shall I make out the check? ',
);
my $check_number = 1000 + int rand 9000;
my $todays_date = strftime( '%B %d, %Y', localtime );
print <<"EOD";
------------------------------------------------------------ Check number $check_number
$todays_date
Pay to the order of ------ $B ----- \$$P
The Memory Bank of New York
The Computer
---------X-----
-------------------------------------------------------------------------------
Come back soon!
EOD
} else {
print <<'EOD';
Thanks for your money.
I'll use it to buy a solid gold roulette wheel
EOD
}
}
{
# Define the kind of each possible spin. 0 is '0' or '00', 1 is
# black, and 2 is red. We assign the values in a BEGIN block because
# execution never actually reaches this point in the script.
my @kind;
BEGIN {
@kind = ( 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1,
2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 0,
0 );
}
# Convert the spin (0-37) to its name on the wheel.
sub format_spin {
my ( $number ) = @_;
state $format = [
sub { '0' x ( $_[0] - 35 ) },
sub { sprintf '%s Black', $_[0] + 1 },
sub { sprintf '%s Red', $_[0] + 1 },
];
return $format->[$kind[$number]]( $number );
}
# Compute the payout given the spin (0-37) and the bet (1-50).
sub payout {
my ( $number, $bet ) = @_;
# We compute the payout on '0' and '00' directly, since under
# our rules they are only eligible for the 35-to-1 bet.
$kind[$number]
or return $number == $bet - 49 + 36 ? 35 : -1;
--$bet; # #bet is 1-based coming in
# Dispatch table for computing the payout for spins 0-36.
state $payout = [
( sub { $_[0] == $_[1] ? 35 : -1 } ) x 36,
( sub { int( $_[0] / 12 ) == $_[1] - 36 ? 2 : -1 } ) x 3,
( sub { $_[0] % 3 == $_[1] - 39 ? 2 : -1 } ) x 3,
( sub { int( $_[0] / 18 ) == $_[1] - 42 ? 1 : -1 } ) x 2,
( sub { $_[0] % 2 == 45 - $_[1] ? 1 : -1 } ) x 2,
( sub { $kind[$_[0]] == 48 - $_[1] ? 1 : -1 } ) x 2,
( sub { -1 } ) x 2, # Bet on '0' or '00' loses
];
return $payout->[$bet]->( $number, $bet );
}
}
# Get input from the user. The arguments are:
# * The prompt
# * A reference to validation code. This code receives the response in
# $ARG and returns true for a valid response.
# * A warning to print if the response is not valid. This must end in a
# return.
# The first valid response is returned. An end-of-file terminates the
# script.
sub get_input {
my ( $prompt, $validate, $warning ) = @ARG;
# If no validator is passed, default to one that always returns
# true.
$validate ||= sub { 1 };
# Create the readline object. The 'state' causes the variable to be
# initialized only once, no matter how many times this subroutine is
# called. The do { ... } is a compound statement used because we
# need to tweak the created object before we store it.
state $term = do {
my $obj = Term::ReadLine->new( 'reverse' );
$obj->ornaments( 0 );
$obj;
};
while ( 1 ) { # Iterate indefinitely
# Read the input into the topic variable, localized to prevent
# Spooky Action at a Distance. We exit on undef, which signals
# end-of-file.
exit unless defined( local $ARG = $term->readline( $prompt ) );
# Return the input if it is valid.
return $ARG if $validate->();
# Issue the warning, and go around the merry-go-round again.
warn $warning;
}
}
# Get a yes-or-no answer. The argument is the prompt, which will have
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
# requested to validate the response as beginning with 'y' or 'n',
# case-insensitive. The return is a true value for 'y' and a false value
# for 'n'.
sub get_yes_no {
my ( $prompt ) = @ARG;
state $map_answer = {
n => 0,
y => 1,
};
my $resp = lc get_input(
"$prompt? [y/n]: ",
sub { m/ \A [yn] /smxi },
"Please respond 'y' or 'n'\n",
);
return $map_answer->{ substr $resp, 0, 1 };
}
__END__
=head1 TITLE
roulette - Play the game 'Roulette' from Basic Computer Games
=head1 SYNOPSIS
roulette.pl
=head1 DETAILS
This Perl script is a port of roulette, which is the 75th
entry in Basic Computer Games.
The main internal changes are converting the roulette slot numbering to
0-based and replacing most of the payout logic with a dispatch table.
These changes were tested for correctness against the original BASIC.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :

View File

@@ -0,0 +1,213 @@
from datetime import date
import random
global RED_NUMBERS
RED_NUMBERS = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36]
def print_instructions():
print("""
THIS IS THE BETTING LAYOUT
(*=RED)
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*
---------------
00 0
TYPES OF BETS
THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET
ON THAT NUMBER.
THESE PAY OFF 35:1
THE 2:1 BETS ARE:
37) 1-12 40) FIRST COLUMN
38) 13-24 41) SECOND COLUMN
39) 25-36 42) THIRD COLUMN
THE EVEN MONEY BETS ARE:
43) 1-18 46) ODD
44) 19-36 47) RED
45) EVEN 48) BLACK
49)0 AND 50)00 PAY OFF 35:1
NOTE: 0 AND 00 DO NOT COUNT UNDER ANY
BETS EXCEPT THEIR OWN.
WHEN I ASK FOR EACH BET, TYPE THE NUMBER
AND THE AMOUNT, SEPARATED BY A COMMA.
FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500
WHEN I ASK FOR A BET.
THE MINIMUM BET IS $5, THE MAXIMUM IS $500.
""")
def query_bets():
"""Queries the user to input their bets"""
betCount = -1
while betCount <= 0:
try:
betCount = int(input("HOW MANY BETS? "))
except:
...
bet_IDs = [-1] * betCount
bet_Values = [0] * betCount
for i in range(betCount):
while(bet_IDs[i] == -1):
try:
inString = input("NUMBER " + str(i + 1) + "? ").split(',')
id,val = int(inString[0]),int(inString[1])
# check other bet_IDs
for j in range(i):
if id != -1 and bet_IDs[j] == id:
id = -1
print("YOU ALREADY MADE THAT BET ONCE, DUM-DUM")
break
if id > 0 and id <= 50 and val >= 5 and val <= 500:
bet_IDs[i] = id
bet_Values[i] = val
except:
...
return bet_IDs,bet_Values
def bet_results(bet_IDs,bet_Values,result):
"""Computes the results, prints them, and returns the total net winnings"""
total_winnings = 0
def get_modifier(id,num):
if id == 37 and num <= 12:
return 2
elif id == 38 and num > 12 and num <= 24:
return 2
elif id == 39 and num > 24 and num < 37:
return 2
elif id == 40 and num < 37 and num % 3 == 1:
return 2
elif id == 41 and num < 37 and num % 3 == 2:
return 2
elif id == 42 and num < 37 and num % 3 == 0:
return 2
elif id == 43 and num <= 18:
return 1
elif id == 44 and num > 18 and num <= 36:
return 1
elif id == 45 and num % 2 == 0:
return 1
elif id == 46 and num % 2 == 1:
return 1
elif id == 47 and num in RED_NUMBERS:
return 1
elif id == 48 and num not in RED_NUMBERS:
return 1
elif id < 37 and id == num:
return 35
else:
return -1
for i in range(len(bet_IDs)):
winnings = bet_Values[i] * get_modifier(bet_IDs[i],result)
total_winnings += winnings
if winnings >= 0:
print("YOU WIN " + str(winnings) + " DOLLARS ON BET " + str(i + 1))
else:
print("YOU LOSE " + str(winnings * -1) + " DOLLARS ON BET " + str(i + 1))
return winnings
def print_check(amount):
"""Prints a check of a given amount"""
name = input("TO WHOM SHALL I MAKE THE CHECK? ")
print("-" * 72)
print()
print(" " * 40 + "CHECK NO. " + str(random.randint(0,100)))
print(" " * 40 + str(date.today()))
print()
print("PAY TO THE ORDER OF -----" + name + "----- $" + str(amount))
print()
print(" " * 40 + "THE MEMORY BANK OF NEW YORK")
print(" " * 40 + "THE COMPUTER")
print(" " * 40 + "----------X-----")
print("-" * 72)
def main():
player_balance = 1000
host_balance = 100000
print(" " * 32 + "ROULETTE")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print()
print()
print()
if stringtobool(input("DO YOU WANT INSTRUCTIONS? ")):
print_instructions()
while True:
bet_IDs,bet_Values = query_bets()
print("SPINNING")
print()
print()
val = random.randint(0,38)
if val == 38:
print("0")
elif val == 37:
print("00")
elif val in RED_NUMBERS:
print(str(val) + " RED")
else:
print(str(val) + " BLACK")
print()
total_winnings = bet_results(bet_IDs,bet_Values,val)
player_balance += total_winnings
host_balance -= total_winnings
print()
print("TOTALS:\tME\t\tYOU")
print("\t\t" + str(host_balance) + "\t" + str(player_balance))
if player_balance <= 0:
print("OOPS! YOU JUST SPENT YOUR LAST DOLLAR!")
break
elif host_balance <= 0:
print("YOU BROKE THE HOUSE!")
player_balance = 101000
break
if not stringtobool(input("PLAY AGAIN? ")):
break
if player_balance <= 0:
print("THANKS FOR YOUR MONEY")
print("I'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL")
else:
print_check(player_balance)
print("COME BACK SOON!")
def stringtobool(string):
"""Converts a string to a bool"""
return string.lower() in ("yes","y","true","t","yes")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,73 @@
puts <<~INSTRUCTIONS
RUSSIAN ROULETTE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THIS IS A GAME OF >>>>>>>>>>RUSSIAN ROULETTE.
HERE IS A REVOLVER.
INSTRUCTIONS
NUMBER_OF_ROUNDS = 9
def parse_input
correct_input = false
while not correct_input
puts " ?"
inp = gets.chomp
if inp == "1" or inp == "2"
correct_input = true
end
end
inp
end
while true
dead = false
n = 0
puts "TYPE \'1\' TO SPIN CHAMBER AND PULL TRIGGER"
puts "TYPE \'2\' TO GIVE UP"
puts "GO"
while not dead
inp = parse_input
if inp == "2"
break
end
if rand > 0.8333333333333334
dead = true
else
puts "- CLICK -"
n += 1
end
if n > NUMBER_OF_ROUNDS
break
end
end
if dead
puts "BANG!!!!! You're Dead!"
puts "Condolences will be sent to your relatives.\n\n\n"
puts "...Next victim..."
else
if n > NUMBER_OF_ROUNDS
puts "You win!!!!!"
puts "Let someone else blow his brain out.\n"
else
puts " Chicken!!!!!\n\n\n"
puts "...Next victim...."
end
end
end

View File

@@ -1,3 +1,23 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
This Perl script is a port of slots, which is the 80th entry in Basic
Computer Games.
I know nothing about slot machines, and my research into them says to me
that the payout tables can be fairly arbitrary. But I have taken the
liberty of deeming the BASIC program's refusal to pay on LEMON CHERRY
LEMON a bug, and made that case a double.
My justification for this is that at the point where the BASIC has
detected the double in the first and third reels it has already detected
that there is no double in the first and second reels. After the check
for a bar (and therefore a double bar) fails it goes back and checks for
a double on the second and third reels. But we know this check will
fail, since the check for a double on the first and second reels failed.
So if a loss was intended at this point, why not just call it a loss?
To restore the original behavior, comment out the entire line commented
'# Bug fix?' (about line 75) and uncomment the line with the trailing
comment '# Bug?' (about line 83).

242
80_Slots/perl/slots.pl Executable file
View File

@@ -0,0 +1,242 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use List::Util qw{ shuffle }; # Shuffle an array.
use Scalar::Util qw{ looks_like_number };
use Term::ReadLine; # Prompt and return user input
our $VERSION = '0.000_01';
print <<'EOD';
SLOTS
Creative Computing Morristown, New Jersey
You are in the H&M casino, in front of one of our
one-arm bandits. Bet from $1 to $100.
To pull the arm, punch the return key after making your bet.
EOD
my $winnings = 0; # Winnings
while ( 1 ) { # Iterate indefinitely
say '';
my $bet = get_input( 'Your bet? ',
sub { m/ \A [0-9]+ \z /smx },
'Please enter a whole number between 0 and 100',
);
if ( $bet > 100 ) {
say 'The house limit is $100';
next;
}
if ( $bet < 1 ) {
say 'The minimum bet is $1';
next;
}
say "\a" x 10;
my $reel_x = int( 6 * rand() );
my $reel_y = int( 6 * rand() );
my $reel_z = int( 6 * rand() );
foreach my $column ( $reel_x, $reel_y, $reel_z ) {
state $symbol = [ qw{ Bar Bell Orange Lemon Plum Cherry } ];
print $symbol->[$column], "\a" x 5, ' ';
}
use constant YOU_WON => 'You won!';
use constant YOU_LOST => 'You lost.';
say '';
if ( $reel_x == $reel_y ) {
if ( $reel_y == $reel_z ) {
if ( $reel_z ) {
say '** TOP DOLLAR **';
$winnings += 11 * $bet;
} else {
say '*** JACKPOT ***';
$winnings += 101 * $bet;
}
say YOU_WON;
} elsif ( $reel_y ) {
$winnings += double( $bet );
} else {
$winnings += double_bar( $bet );
}
} elsif ( $reel_x == $reel_z ) {
if ( $reel_z ) {
$winnings += double( $bet ); # Bug fix?
# NOTE that the below code is what is actually implemented
# in the basic, but it is implemented strangely enough (a
# GOTO a line that contains a test that, if I understand the
# control flow, must fail) that I wonder if it is an error.
# I know nothing about slot machines, but research suggests
# the payoff table is fairly arbitrary. The code above makes
# code above makes the game orthogonal.
# $winnings += you_lost( $bet ); # Bug?
} else {
$winnings += double_bar( $bet );
}
} elsif ( $reel_y == $reel_z ) {
if ( $reel_z ) {
$winnings += double( $bet );
} else {
$winnings += double_bar( $bet );
}
} else {
$winnings += you_lost( $bet );
}
say 'Your standings are $', $winnings;
last unless get_yes_no( 'Again' );
}
if ( $winnings < 0 ) {
say 'Pay up! Please leave your money on the terminal.';
} elsif ( $winnings > 0 ) {
say 'Collect your winnings from the H&M cashier.';
} else {
say 'Hey, you broke even.';
}
sub double {
my ( $bet ) = @_;
say 'DOUBLE!';
say YOU_WON;
return 3 * $bet;
}
sub double_bar {
my ( $bet ) = @_;
say '* DOUBLE BAR *';
say YOU_WON;
return 6 * $bet;
}
sub you_lost {
my ( $bet ) = @_;
say YOU_LOST;
return -$bet;
}
# Get input from the user. The arguments are:
# * The prompt
# * A reference to validation code. This code receives the response in
# $ARG and returns true for a valid response.
# * A warning to print if the response is not valid. This must end in a
# return.
# The first valid response is returned. An end-of-file terminates the
# script.
sub get_input {
my ( $prompt, $validate, $warning ) = @ARG;
# If no validator is passed, default to one that always returns
# true.
$validate ||= sub { 1 };
# Create the readline object. The 'state' causes the variable to be
# initialized only once, no matter how many times this subroutine is
# called. The do { ... } is a compound statement used because we
# need to tweak the created object before we store it.
state $term = do {
my $obj = Term::ReadLine->new( 'reverse' );
$obj->ornaments( 0 );
$obj;
};
while ( 1 ) { # Iterate indefinitely
# Read the input into the topic variable, localized to prevent
# Spooky Action at a Distance. We exit on undef, which signals
# end-of-file.
exit unless defined( local $ARG = $term->readline( $prompt ) );
# Return the input if it is valid.
return $ARG if $validate->();
# Issue the warning, and go around the merry-go-round again.
warn $warning;
}
}
# Get a yes-or-no answer. The argument is the prompt, which will have
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
# requested to validate the response as beginning with 'y' or 'n',
# case-insensitive. The return is a true value for 'y' and a false value
# for 'n'.
sub get_yes_no {
my ( $prompt ) = @ARG;
state $map_answer = {
n => 0,
y => 1,
};
my $resp = lc get_input(
"$prompt? [y/n]: ",
sub { m/ \A [yn] /smxi },
"Please respond 'y' or 'n'\n",
);
return $map_answer->{ substr $resp, 0, 1 };
}
__END__
=head1 TITLE
slots - Play the game 'Slots' from Basic Computer Games
=head1 SYNOPSIS
slots.pl
=head1 DETAILS
This Perl script is a port of C<slots>, which is the 80th entry in Basic
Computer Games.
I know nothing about slot machines, and my research into them says to me
that the payout tables can be fairly arbitrary. But I have taken the
liberty of deeming the BASIC program's refusal to pay on LEMON CHERRY
LEMON a bug, and made that case a double.
My justification for this is that at the point where the BASIC has
detected the double in the first and third reels it has already detected
that there is no double in the first and second reels. After the check
for a bar (and therefore a double bar) fails it goes back and checks for
a double on the second and third reels. But we know this check will
fail, since the check for a double on the first and second reels failed.
So if a loss was intended at this point, why not just call it a loss?
To restore the original behavior, comment out the entire line commented
C<'# Bug fix?'> (about line 75) and uncomment the line with the trailing
comment C<'# Bug?'> (about line 83).
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :

152
80_Slots/ruby/slots.rb Normal file
View File

@@ -0,0 +1,152 @@
$pot = 0
def greeting
puts "SLOTS".center(80)
puts "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".center(80)
puts "\n\n"
# PRODUCED BY FRED MIRABELLE AND BOB HARPER ON JAN 29, 1973
# IT SIMULATES THE SLOT MACHINE.
puts "You are in the H&M Casino, in front of one of our"
puts "one-arm bandits. You can bet from $1 to $100."
puts "To pull the arm, punch the return key after making your bet."
puts "\nBet zero to end the game."
end
def overLimit
puts "House Limit is $100"
end
def underMinimum
puts "Minimum Bet is $1"
end
# bells don't work on my machine. YMMV
# I'm displaying dashes between the reels
def tenBells
10.times do
# beep if you can
print "-"
end
end
def fiveBells
"-----"
end
def goodbye
puts "\n\n\n"
# end the game
exit
end
def payUp
puts "PAY UP! PLEASE LEAVE YOUR MONEY ON THE TERMINAL."
end
def brokeEven
puts "HEY, YOU BROKE EVEN."
end
def collectWinnings
puts "COLLECT YOUR WINNINGS FROM THE H&M CASHIER."
end
def win winType, bet
case winType
when "jackpot"
winMessage = "***JACKPOT***"
winnings = 101
when "topDollar"
winMessage = "**TOP DOLLAR**"
winnings = 11
when "doubleBar"
winMessage = "*DOUBLE BAR!!*"
winnings = 6
when "double"
winMessage = "DOUBLE!!"
winnings = 3
end
puts "\nYou won: " + winMessage
$pot += (winnings * bet)
end
greeting
#$pot = 0
while true
reelArray = ["BAR","BELL","ORANGE","LEMON","PLUM","CHERRY"]
print "\nYOUR BET? "
# get input, remove leading and trailing whitespace, cast to integer
bet = gets.strip.to_i
if bet == 0 then
goodbye
elsif bet > 100 then
overLimit # error if more than $100
elsif bet < 1 then
underMinimum # have to bet at least a dollar
else
# valid bet, continue
tenBells # ding
# assign a random value from the array to each of the three reels
reel1 = reelArray[rand(5)]
reel2 = reelArray[rand(5)]
reel3 = reelArray[rand(5)]
# print the slot machine reels
puts "\n\n" + reel1 + fiveBells + reel2 + fiveBells + reel3
# see if we have a match in the first two reels
if reel1 == reel2 then
if reel2 == reel3 then
if reel3 == "BAR" then
# all three reels are "BAR"
win "jackpot", bet
else
# all three reels match but aren't "BAR"
win "topDollar", bet
end
elsif reel2 == "BAR" then
# reels 1 and 2 are both "BAR"
win "doubleBar", bet
else
# reels 1 and 2 match but aren't "BAR"
win "double", bet
end
# otherwise see if there's a match in the remaining reels
elsif reel1 == reel3 or reel2 == reel3 then
if reel3 == "BAR" then
# two reels match, both "BAR"
win "doubleBar", bet
else
# two reels match, but not "BAR"
win "double", bet
end
else
# bad news - no matches
puts "\nYou lost"
# decrement your standings by the bet amount
$pot -= bet
end
puts "Your standings are: " + $pot.to_s
print "\nAgain? " # YES to continue
# get input, remove leading and trailing whitespace, make uppercase
again = gets.strip.upcase
if again != "Y" && again != "YES" then
# that's enough... evaluate the pot and quit
if $pot < 0 then
payUp
elsif $pot == 0 then
brokeEven
else # yay!
collectWinnings
end
goodbye
end
end
end

View File

@@ -39,8 +39,7 @@ public class Splat {
System.out.printf(" ACCELERATION = %.2f FT/SEC/SEC +/-5%%\n", initial.getOriginalAcceleration());
System.out.println("SET THE TIMER FOR YOUR FREEFALL.");
System.out.print("HOW MANY SECONDS ");
float freefallTime = scanner.nextFloat();
float freefallTime = promptFloat("HOW MANY SECONDS ");
System.out.println("HERE WE GO.\n");
System.out.println("TIME (SEC) DIST TO FALL (FT)");
System.out.println("========== =================");
@@ -73,18 +72,28 @@ public class Splat {
private float promptTerminalVelocity() {
if (askYesNo("SELECT YOUR OWN TERMINAL VELOCITY")) {
System.out.print("WHAT TERMINAL VELOCITY (MI/HR) ");
return mphToFeetPerSec(scanner.nextFloat());
float terminalVelocity = promptFloat("WHAT TERMINAL VELOCITY (MI/HR) ");
return mphToFeetPerSec(terminalVelocity);
}
float terminalVelocity = (int) (1000 * random.nextFloat());
System.out.printf("OK. TERMINAL VELOCITY = %.2f MI/HR\n", terminalVelocity);
return mphToFeetPerSec(terminalVelocity);
}
private float promptFloat(String prompt){
while(true){
System.out.print(prompt);
try {
return scanner.nextFloat();
} catch (Exception e) {
scanner.next(); // clear current input
}
}
}
private float promptGravitationalAcceleration() {
if (askYesNo("WANT TO SELECT ACCELERATION DUE TO GRAVITY")) {
System.out.print("WHAT ACCELERATION (FT/SEC/SEC) ");
return scanner.nextFloat();
return promptFloat("WHAT ACCELERATION (FT/SEC/SEC) ");
}
return chooseRandomAcceleration();
}

336
81_Splat/perl/splat.pl Executable file
View File

@@ -0,0 +1,336 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use List::Util qw{ shuffle }; # Shuffle an array.
use Scalar::Util qw{ looks_like_number };
use Term::ReadLine; # Prompt and return user input
our $VERSION = '0.000_01';
use constant ROW_TPLT => ( '%4d' x 8 ) . "\n";
print <<'EOD';
SPLAT
Creative Computing Morristown, New Jersey
Welcome to 'Splat' -- the game that simulates a parachute
jump. Try to open your chute at the last possible
moment without going splat.
EOD
while ( 1 ) {
say '';
my $initial_altitude = int( 9001 * rand() + 1000 );
my $nominal_terminal_velocity;
if ( get_yes_no( 'Select your own terminal velocity' ) ) {
$nominal_terminal_velocity = get_input(
'What terminal velocity (mi/hr)? ',
sub { looks_like_number( $ARG ) && $ARG > 0 },
'Please enter a positive number',
);
# Convert miles per hour to feet per second
$nominal_terminal_velocity = $nominal_terminal_velocity * 5280 / 3600;
} else {
$nominal_terminal_velocity = int( 1000 * rand() );
say "OK. Terminal velocity = $nominal_terminal_velocity mi/hr"
}
my $terminal_velocity = dither( $nominal_terminal_velocity );
my $nominal_gravity; # Acceleration due to gravity
if ( get_yes_no( 'Want to select acceleration due to gravity' ) ) {
} else {
state $body = [
[ q<Fine. You're on Mercury. Acceleration = 12.2 ft/sec/sec.>,
12.2 ],
[ q<All right. You're on Venus. Acceleration = 28.3 ft/sec/sec.>,
28.3 ],
[ q<Then you're on Earth. Acceleration = 32.16 ft/sec/sec.>,
32.16 ],
[ q<Fine. You're on the Moon. Acceleration = 5.15 ft/sec/sec.>,
5.15 ],
[ q<All right. You're on Mars. Acceleration = 12.5 ft/sec/sec.>,
12.5 ],
[ q<Then you're on Jupiter. Acceleration = 85.2 ft/sec/sec.>,
85.2 ],
[ q<Fine. You're on Saturn. Acceleration = 37.6 ft/sec/sec.>,
37.6 ],
[ q<All right. You're on Uranus. Acceleration = 33.8 ft/sec/sec.>,
33.8 ],
[ q<Then you're on Neptune. Acceleration = 39.6 ft/sec/sec.>,
39.6 ],
[ q<Fine. You're on the Sun. Acceleration = 896 ft/sec/sec.>,
896 ],
];
my $pick = $body->[ rand scalar @{ $body } ];
say $pick->[0];
$nominal_gravity = $pick->[1];
}
my $gravity = dither( $nominal_gravity );
print <<"EOD";
Altitude = $initial_altitude ft
Term. velocity = $nominal_terminal_velocity ft/sec +/- 5%
Acceleration = $nominal_gravity ft/sec/sec +/- 5%
Set the timer for your freefall
EOD
my $drop_time = get_input(
'How many seconds? ',
sub { m/ \A [0-9]+ \z /smx },
"Please enter an unsigned integer\n",
);
print <<'EOD';
Here we go.
Time (sec) Dist to fall (ft)
========== =================
EOD
if ( defined( my $altitude = make_jump(
$initial_altitude,
$gravity,
$terminal_velocity,
$drop_time ) )
) {
# Successful jump
state $succesful = [];
state $ordinal = [ qw{ 1st 2nd 3rd } ];
if ( defined( my $ord = $ordinal->[ @{ $succesful } ] ) ) {
say "Amazing!!! Not nad for your $ord successful jump!!!";
} else {
my $jumps = @{ $succesful };
my $worse = grep { $_ > $altitude } @{ $succesful };
my $fractile = 1 - $worse / $jumps;
my $better = $jumps - $worse;
if ( $fractile <= 0.1 ) {
print <<"EOD";
Wow! That's some jumping. Of the $jumps successful jumps
before yours, only $better opened their chutes lower than
you did.
EOD
} elsif ( $fractile <= 0.25 ) {
print <<"EOD";
Pretty good! $jumps successful jumps preceded yours and only
$better of them got lower than you did before their chutes
opened.
EOD
} elsif ( $fractile <= 0.5 ) {
print <<"EOD";
Not bad. There have been $jumps successful jumps before yours.
You were beaten out by $better of them.
EOD
} elsif ( $fractile <= 0.75 ) {
print <<"EOD";
Conservative, aren't you? You ranked only $better in the
$jumps successful jumps before yours.
EOD
} elsif ( $fractile <= 0.9 ) {
print <<"EOD";
Humph! Don't you have any sporting blood? There were
$jumps successful jumps before yours and you came in $worse jumps
better than the worst. Shape up!!!
EOD
} else {
print <<"EOD";
Hey! You pulled the rip cord much too soon. $jumps successful
jumps before yours and you came in number $better! Get with it!
EOD
}
}
push @{ $succesful }, $altitude;
} else {
# Splat
say q<I'll give you another chance.>;
}
next if get_yes_no( 'Do you want to play again' );
next if get_yes_no( 'Please' );
print <<'EOD';
Ssssssssss.
EOD
last;
}
# Return the first argument modified by up to plus or minus some
# fraction specified by the second argument (default 0.05)
sub dither {
my ( $arg, $fract ) = @_;
$fract //= 1 / 20;
return $arg + ( $arg * rand() * $fract ) - ( $arg * rand() * $fract );
}
use constant FORMAT_FALL => "%10.1f %10d\n";
use constant FORMAT_SPLAT => "%10.1f %s\n";
sub make_jump {
my ( $initial_altitude, $gravity, $terminal_velocity, $drop_time ) = @_;
my $altitude;
foreach my $step ( 0 .. 8 ) {
my $time = $step * $drop_time / 8;
if ( $time > $terminal_velocity / $gravity ) {
# Terminal velocity reached
printf "Terminal velocity reached at T plus %.2f seconds.\n",
$terminal_velocity / $gravity;
for my $step ( $step .. 8 ) {
my $time = $step * $drop_time / 8;
$altitude = $initial_altitude - (
$terminal_velocity * $terminal_velocity /
( 2 * $gravity ) + $terminal_velocity * (
$time - $terminal_velocity / $gravity ) );
if ( $altitude > 0 ) {
printf FORMAT_FALL, $time, $altitude;
} else {
splat(
$terminal_velocity / $gravity + (
$initial_altitude -
$terminal_velocity * $terminal_velocity /
( 2 * $gravity ) ) / $terminal_velocity,
);
return;
}
}
last;
} else {
$altitude = $initial_altitude - $gravity / 2 * $time * $time;
if ( $altitude > 0 ) {
printf FORMAT_FALL, $time, $altitude;
} else {
splat( sqrt( 2 * $initial_altitude / $gravity ) );
return;
}
}
}
say 'Chute open.';
return $altitude;
}
sub splat {
my ( $time ) = @_;
printf FORMAT_SPLAT, $time, 'Splat!';
state $rip = [
q<Requiescat in pace.>,
q<May the angel of heaven lead you into paradise.>,
q<Rest in peace.>,
q<Son-of-a-gun.>,
q<#$%&&%!$>,
q<A kick in the pants is a boost if you're headed right.>,
q<Hmmm. Should have picked a shorter time.>,
q<Mutter. Mutter. Mutter.>,
q<Pushing up daisies.>,
q<Easy come, easy go.>,
];
say $rip->[ rand scalar @{ $rip } ];
return;
}
# Get input from the user. The arguments are:
# * The prompt
# * A reference to validation code. This code receives the response in
# $ARG and returns true for a valid response.
# * A warning to print if the response is not valid. This must end in a
# return.
# The first valid response is returned. An end-of-file terminates the
# script.
sub get_input {
my ( $prompt, $validate, $warning ) = @ARG;
# If no validator is passed, default to one that always returns
# true.
$validate ||= sub { 1 };
# Create the readline object. The 'state' causes the variable to be
# initialized only once, no matter how many times this subroutine is
# called. The do { ... } is a compound statement used because we
# need to tweak the created object before we store it.
state $term = do {
my $obj = Term::ReadLine->new( 'reverse' );
$obj->ornaments( 0 );
$obj;
};
while ( 1 ) { # Iterate indefinitely
# Read the input into the topic variable, localized to prevent
# Spooky Action at a Distance. We exit on undef, which signals
# end-of-file.
exit unless defined( local $ARG = $term->readline( $prompt ) );
# Return the input if it is valid.
return $ARG if $validate->();
# Issue the warning, and go around the merry-go-round again.
warn $warning;
}
}
# Get a yes-or-no answer. The argument is the prompt, which will have
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
# requested to validate the response as beginning with 'y' or 'n',
# case-insensitive. The return is a true value for 'y' and a false value
# for 'n'.
sub get_yes_no {
my ( $prompt ) = @ARG;
state $map_answer = {
n => 0,
y => 1,
};
my $resp = lc get_input(
"$prompt? [y/n]: ",
sub { m/ \A [yn] /smxi },
"Please respond 'y' or 'n'\n",
);
return $map_answer->{ substr $resp, 0, 1 };
}
__END__
=head1 TITLE
splat.pl - Play the game 'splat' from Basic Computer Games
=head1 SYNOPSIS
splat.pl
=head1 DETAILS
This Perl script is a port of C<splat>, which is the 73rd entry in
Basic Computer Games.
This is a very basic port. All I really did was untangle the spaghetti.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :

View File

@@ -0,0 +1,208 @@
import random
#Stock_Market
class Stock_Market():
def __init__(self):
#Hard Coded Names
short_names = ['IBM', 'RCA', 'LBJ', 'ABC', 'CBS']
full_names = ['INT. BALLISTIC MISSLES', 'RED CROSS OF AMERICA',
'LICHTENSTEIN, BUMRAP & JOKE', 'AMERICAN BANKRUPT CO.',
'CENSURED BOOKS STORE']
#Initializing Dictionary to hold all the information systematically
self.data = {}
for sn, fn in zip(short_names, full_names):
#A dictionary for each stock
temp = {'Name' : fn, 'Price' : None, 'Holdings' : 0}
#Nested outer dictionary for all stocks
self.data[sn] = temp
#Initializing Randomly generated initial prices
for stock in self.data.values():
stock['Price'] = round(random.uniform(80,120),2) #Price b/w 60 and 120
#Initialize Assets
self.cash_assets = 10000
self.stock_assets = 0
def total_assets(self):
return self.cash_assets + self.stock_assets
def _generate_day_change(self):
self.changes = []
for _ in range(len(self.data)):
self.changes.append(round(random.uniform(-5,5),2)) #Random % Change b/w -5 and 5
def update_prices(self):
self._generate_day_change()
for stock, change in zip(self.data.values(), self.changes):
stock['Price'] = round(stock['Price'] + (change/100)*stock['Price'], 2)
def print_exchange_average(self):
sum = 0
for stock in self.data.values():
sum += stock['Price']
print('\nNEW YORK STOCK EXCHANGE AVERAGE: ${:.2f}'.format(sum/5))
def get_average_change(self):
sum = 0
for change in self.changes:
sum += change
return round(sum/5,2)
def print_first_day(self):
print('\nSTOCK\t\t\t\t\tINITIALS\tPRICE/SHARE($)')
for stock, data in self.data.items():
if stock != 'LBJ':
print('{}\t\t\t{}\t\t{}'.format(data['Name'], stock, data['Price']))
else:
print('{}\t\t{}\t\t{}'.format(data['Name'], stock, data['Price']))
self.print_exchange_average()
self.print_assets()
def take_inputs(self):
print('\nWHAT IS YOUR TRANSACTION IN')
flag = False
while flag != True:
new_holdings = []
for stock in self.data.keys():
try:
new_holdings.append(int(input('{}? '.format(stock))))
except:
print('\nINVALID ENTRY, TRY AGAIN\n')
break
if len(new_holdings) == 5:
flag = self._check_transaction(new_holdings)
return new_holdings
def print_trading_day(self):
print("STOCK\tPRICE/SHARE\tHOLDINGS\tNET. Value\tPRICE CHANGE")
for stock, data, change in zip(self.data.keys(), self.data.values(),self.changes):
value = data['Price'] * data['Holdings']
print('{}\t{}\t\t{}\t\t{:.2f}\t\t{}'.format(stock, data['Price'], data['Holdings'], value, change))
def update_cash_assets(self, new_holdings):
sell=0
buy=0
for stock, holding in zip(self.data.values(), new_holdings):
if holding > 0:
buy += stock['Price']*holding
elif holding < 0:
sell += stock['Price']*abs(holding)
self.cash_assets = self.cash_assets + sell - buy
def update_stock_assets(self):
sum=0
for data in self.data.values():
sum += data['Price']*data['Holdings']
self.stock_assets = round(sum,2)
def print_assets(self):
print('\nTOTAL STOCK ASSETS ARE: ${:.2f}'.format(self.stock_assets))
print('TOTAL CASH ASSETS ARE: ${:.2f}'.format(self.cash_assets))
print('TOTAL ASSETS ARE: ${:.2f}'.format(self.total_assets()))
def _check_transaction(self, new_holdings):
sum = 0
for stock, holding in zip(self.data.values(), new_holdings):
if holding > 0:
sum += stock['Price']*holding
elif holding < 0:
if abs(holding) > stock['Holdings']:
print('\nYOU HAVE OVERSOLD SOME STOCKS, TRY AGAIN\n')
return False
if sum > self.cash_assets:
print('\nYOU HAVE USED ${:.2f} MORE THAN YOU HAVE, TRY AGAIN\n'.format(sum - self.cash_assets))
return False
return True
def update_holdings(self, new_holdings):
for stock, new_holding in zip(self.data.values(), new_holdings):
stock['Holdings'] += new_holding
def print_instruction():
print('''
THIS PROGRAM PLAYS THE STOCK MARKET. YOU WILL BE GIVEN
$10,000 AND MAY BUY OR SELL STOCKS. THE STOCK PRICES WILL
BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT
REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE. A TABLE
OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES
IN YOUR PORTFOLIO WILL BE PRINTED. FOLLOWING THIS, THE
INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION
MARK. HERE YOU INDICATE A TRANSACTION. TO BUY A STOCK
TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE
NUMBER OF SHARES. A BROKERAGE FEE OF 1% WILL BE CHARGED
ON ALL TRANSACTIONS. NOTE THAT IF A STOCK'S VALUE DROPS
TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN. YOU
HAVE $10,000 TO INVEST. USE INTEGERS FOR ALL YOUR INPUTS.
(NOTE: TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST
10 DAYS)
------------GOOD LUCK!------------\n
''')
if __name__ == "__main__":
print('\t\t STOCK MARKET')
help = input('\nDO YOU WANT INSTRUCTIONS(YES OR NO)? ')
#Printing Instruction
if help == 'YES' or help == 'yes' or help == 'Yes':
print_instruction()
#Initialize Game
Game = Stock_Market()
#Do first day
Game.print_first_day()
new_holdings = Game.take_inputs()
Game.update_holdings(new_holdings)
Game.update_cash_assets(new_holdings)
print('\n------------END OF TRADING DAY--------------\n')
response = 1
while response == 1:
#Simulate a DAY
Game.update_prices()
Game.print_trading_day()
Game.print_exchange_average()
Game.update_stock_assets()
Game.print_assets()
response = int(input('\nDO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)? '))
if response == 0:
break
new_holdings = Game.take_inputs()
Game.update_holdings(new_holdings)
Game.update_cash_assets(new_holdings)
print('\n------------END OF TRADING DAY--------------\n')
print('\nHOPE YOU HAD FUN!!!!')
input('')

View File

@@ -0,0 +1,94 @@
########################################################
#
# Synonym
#
# From Basic Computer Games (1978)
#
# A synonym of a word is another word (in the English language) which has the same,
# or very nearly the same, meaning. This program tests your knowledge of synonyms
# of a few common words.
#
# The computer chooses a word and asks you for a synonym. The computer then tells
# you whether youre right or wrong. If you cant think of a synonym, type “HELP”
# which causes a synonym to be printed.
# You may put in words of your choice in the data statements.
# The number following DATA in Statement 500 is the total number of data statements.
# In each data statement, the first number is the number of words in that statement.
#
# Can you think of a way to make this into a more general kind of CAI program for any subject?
# Walt Koetke of Lexington High School, Massachusetts created this program.
#
#
########################################################
puts <<~INSTRUCTIONS
SYNONYM
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH
LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME MEANING.
I CHOOSE A WORD -- YOU TYPE A SYNONYM.
IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'
AND I WILL TELL YOU A SYNONYM.
INSTRUCTIONS
right_words = ["RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"]
synonym_words = [
["FIRST", "START", "BEGINNING", "ONSET", "INITIAL"],
["SIMILAR", "ALIKE", "SAME", "LIKE", "RESEMBLING"],
["MODEL", "PATTERN", "PROTOTYPE", "STANDARD", "CRITERION"],
["SMALL", "INSIGNIFICANT", "LITTLE", "TINY", "MINUTE"],
["STOP", "HALT", "STAY", "ARREST", "CHECK", "STANDSTILL"],
["HOUSE", "DWELLING", "RESIDENCE", "DOMICILE", "LODGING", "HABITATION"],
["PIT", "HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS"],
["PUSH", "SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS"],
["RED", "ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY"],
["PAIN", "SUFFERING", "HURT", "MISERY", "DISTRESS", "ACHE", "DISCOMFORT"],
]
synonym_words.shuffle.each {|words_ar|
}
synonym_words.each {|words_ar|
answer = false
keyword = words_ar.shift
while not answer and words_ar.length != 0
puts " WHAT IS A SYNONYM OF #{keyword}? "
inp = gets.chomp.upcase
if inp == "HELP"
clue = words_ar.sample
puts "**** A SYNONYM OF #{keyword} IS #{clue}."
words_ar.delete(clue)
elsif words_ar.include? inp
puts right_words.sample
answer = true
else
puts "TRY AGAIN."
end
end
}
puts "SYNONYM DRILL COMPLETED"
######################################################################
#
# Porting notes
#
# There is a bug in the original program where if you keep asking for
# synoyms of a given word it ends up running out of synonyms
# in the array and the program crashes.
# The bug has been fixed in this version and now when
# it runs out of words it continues with the next
# array.
#
######################################################################

2
87_3-D_Plot/d/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
*.obj

182
87_3-D_Plot/d/README.md Normal file
View File

@@ -0,0 +1,182 @@
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
## Running the code
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
```shell
dmd -dip1000 -run threedeeplot.d
```
[Other compilers](https://dlang.org/download.html) also exist.
## On rounding floating point values to integer values
The D equivalent of Basic `INT` is [`floor`](https://dlang.org/phobos/std_math_rounding.html#.floor),
which rounds towards negative infinity. If you change occurrences of `floor` to
[`lrint`](https://dlang.org/phobos/std_math_rounding.html#.lrint), you'll see that the plots show a bit more detail,
as is done in the bonus below.
## Bonus: Self-writing programs
With a small modification to the source, the program can be extended to **plot a random function**, and **print its formula**.
```shell
rdmd -dip1000 threedeeplot_random.d
```
(`rdmd` caches the executable, which results in speedy execution when the source does not change.)
### Example output
```
3D Plot
(After Creative Computing Morristown, New Jersey)
f(z) = 30 * sin(z / 10.0)
*
* * * *
* * * * *
* * * * *
* * * * * *
* * * * * *
* * * * * **
* * * * * * *
* * * * * * **
* * * * * * * *
* * * * * * * *
* * * * * * * **
* * * * * * * * *
* * * * ** * * * *
* * * * ** * * *
* * * * * * * *
* * * * * * * *
* * * ** * * **
* * * ** * **
* * * * * **
* * * * * **
* * * * * **
* * * ** * **
* * * ** * * **
* * * * * * * *
* * * * * * * *
* * * * ** * * *
* * * * ** * * * *
* * * * * * * * *
* * * * * * * **
* * * * * * * *
* * * * * * * *
* * * * * * **
* * * * * * *
* * * * * **
* * * * * *
* * * * * *
* * * * *
* * * * *
* * * *
*
```
### Breakdown of differences
Have a look at the relevant differences between `threedeeplot.d` and `threedeeplot_random.d`.
This is the original function with the single expression that is evaluated for the plot:
```d
static float fna(float z)
{
return 30.0 * exp(-z * z / 100.0);
}
```
Here `static` means that the nested function does not need acces to its enclosing scope.
Now, by inserting the following:
```d
enum functions = ["30.0 * exp(-z * z / 100.0)",
"sqrt(900.01 - z * z) * .9 - 2",
"30 * (cos(z / 16.0) + .5)",
"30 - 30 * sin(z / 18.0)",
"30 * exp(-cos(z / 16.0)) - 30",
"30 * sin(z / 10.0)"];
size_t index = uniform(0, functions.length);
writeln(center("f(z) = " ~ functions[index], width), "\n");
```
and changing the implementation of `fna` to
```d
float fna(float z)
{
final switch (index)
{
static foreach (i, f; functions)
case i:
mixin("return " ~ f ~ ";");
}
}
```
we unlock some very special abilities of D. Let's break it down:
```d
enum functions = ["30.0 * exp(-z * z / 100.0)", /*...*/];
```
This defines an array of strings, each containing a mathematical expression. Due to the `enum` keyword, this is an
array that really only exists at compile-time.
```d
size_t index = uniform(0, functions.length);
```
This defines a random index into the array. `functions.length` is evaluated at compile-time, due to D's compile-time
function evaluation (CTFE).
```d
writeln(center("f(z) = " ~ functions[index], width), "\n");
```
Unmistakenly, this prints the formula centered on a line. What happens behind the scenes is that `functions` (which
only existed at compile-time before now) is pasted in, so that an instance of that array actually exists at run-time
at this spot, and is instantly indexed.
```d
float fna(float z)
{
final switch (index)
{
// ...
}
}
```
`static` has been dropped from the nested function because we want to evaluate `index` inside it. The function contains
an ordinary `switch`, with `final` providing some extra robustness. It disallows a `default` case and produces an error
when the switch doesn't handle all cases. The `switch` body is where the magic happens and consists of these three
lines:
```d
static foreach (i, f; functions)
case i:
mixin("return " ~ f ~ ";");
```
The `static foreach` iterates over `functions` at compile-time, producing one `case` for every element in `functions`.
`mixin` takes a string, which is constructed at compile-time, and pastes it right into the source.
In effect, the implementation of `float fna(float z)` unrolls itself into
```d
float fna(float z)
{
final switch (index)
{
case 0:
return 30.0 * exp(-z * z / 100.0);
case 1:
return sqrt(900.01 - z * z) * .9 - 2;
case 2:
return 30 * (cos(z / 16.0) + .5);
case 3:
return 30 - 30 * sin(z / 18.0);
case 4:
return 30 * exp(-cos(z / 16.0)) - 30;
case 5:
return 30 * sin(z / 10.0)";
}
}
```
So if you feel like adding another function, all you need to do is append it to the `functions` array, and the rest of
the program *rewrites itself...*

View File

@@ -0,0 +1,35 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
import std.stdio, std.string, std.math, std.range, std.conv, std.algorithm;
void main()
{
enum width = 80;
writeln(center("3D Plot", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n\n", width));
static float fna(float z)
{
return 30.0 * exp(-z * z / 100.0);
}
char[] row;
for (float x = -30.0; x <= 30.0; x += 1.5)
{
size_t max_z = 0L;
auto y1 = 5 * floor((sqrt(900 - x * x)) / 5.0);
for (float y = y1; y >= -y1; y -= 5)
{
auto z = to!size_t(max(0, floor(25 + fna(sqrt(x * x + y * y)) - .7 * y)));
if (z > max_z) // Visible
{
max_z = z;
if (z + 1 > row.length) // row needs to grow
row ~= ' '.repeat(z + 1 - row.length).array;
row[z] = '*';
}
}
writeln(row);
row = null;
}
}

View File

@@ -0,0 +1,50 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
import std.stdio, std.string, std.math, std.range, std.conv, std.random, std.algorithm;
void main()
{
enum width = 80;
writeln(center("3D Plot", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n", width));
enum functions = ["30.0 * exp(-z * z / 100.0)",
"sqrt(900.01 - z * z) * .9 - 2",
"30 * (cos(z / 16.0) + .5)",
"30 - 30 * sin(z / 18.0)",
"30 * exp(-cos(z / 16.0)) - 30",
"30 * sin(z / 10.0)"];
size_t index = uniform(0, functions.length);
writeln(center("f(z) = " ~ functions[index], width), "\n");
float fna(float z)
{
final switch (index)
{
static foreach (i, f; functions)
case i:
mixin("return " ~ f ~ ";");
}
}
char[] row;
for (float x = -30.0; x <= 30.0; x += 1.5)
{
size_t max_z = 0L;
auto y1 = 5 * lrint((sqrt(900 - x * x)) / 5.0);
for (float y = y1; y >= -y1; y -= 5)
{
auto z = to!size_t(max(0, lrint(25 + fna(sqrt(x * x + y * y)) - .7 * y)));
if (z > max_z) // Visible
{
max_z = z;
if (z + 1 > row.length) // row needs to grow
row ~= ' '.repeat(z + 1 - row.length).array;
row[z] = '*';
}
}
writeln(row);
row = null;
}
}

View File

@@ -0,0 +1,75 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
// Print text on the screen with 30 spaces before text
Console.WriteLine("TIC TAC TOE".PadLeft(30));
// Print text on screen with 15 spaces before text
Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".PadLeft(15));
// Print three blank lines on screen
Console.WriteLine("\n\n\n");
// THIS PROGRAM PLAYS TIC TAC TOE
// THE MACHINE GOES FIRST
Console.WriteLine("THE GAME BOARD IS NUMBERED:\n");
Console.WriteLine("1 2 3");
Console.WriteLine("8 9 4");
Console.WriteLine("7 6 5");
// Main program
while(true) {
int a, b, c, d, e;
int p, q, r, s;
a = 9;
Console.WriteLine("\n\n");
computerMoves(a);
p = readYourMove();
b = move(p + 1);
computerMoves(b);
q = readYourMove();
if (q == move(b + 4)) {
c = move(b + 2);
computerMoves(c);
r = readYourMove();
if (r == move(c + 4)) {
if (p % 2 != 0) {
d = move(c + 3);
computerMoves(d);
s = readYourMove();
if (s != move(d + 4)) {
e = move(d + 4);
computerMoves(e);
}
e = move(d + 6);
computerMoves(e);
Console.WriteLine("THE GAME IS A DRAW.");
} else {
d = move(c + 7);
computerMoves(d);
Console.WriteLine("AND WINS ********");
}
} else {
d = move(c + 4);
computerMoves(d);
Console.WriteLine("AND WINS ********");
}
} else {
c = move(b + 4);
computerMoves(c);
Console.WriteLine("AND WINS ********");
}
}
void computerMoves(int move) {
Console.WriteLine("COMPUTER MOVES " + move);
}
int readYourMove() {
while(true) {
Console.Write("YOUR MOVE?");
string input = Console.ReadLine();
if (int.TryParse(input, out int number)) {
return number;
}
}
}
int move(int number) {
return number - 8 * (int)((number - 1) / 8);
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,205 @@
#!/usr/bin/perl
use strict;
use warnings;
#GLOBALs
my %board = (
1 => 0,
2 => 0,
3 => 0,
4 => 0,
5 => 0,
6 => 0,
7 => 0,
8 => 0,
9 => 0,
);
my %winning_combos = (
1 => [1,2,3],
2 => [4,5,6],
3 => [7,8,9],
4 => [1,4,7],
5 => [2,5,8],
6 => [3,6,9],
7 => [1,5,9],
8 => [7,5,3],
);
my $player=100;
my $player_goal=0;
my $computer=100;
my $computer_goal=0;
my $count=0;
&main;
sub main {
&print_intro;
print "DO YOU WANT 'X' OR 'O'\n";
chomp(my $ans = <STDIN>);
&assign_X_and_O($ans);
if ($ans eq "X") {
until ($count >= 9) {
&player_choice;
$count++;
&print_board;
&check_for_winners;
&computer_choice;
$count++;
&print_board;
&check_for_winners;
}
}
else {
until ($count >= 9) {
&computer_choice;
$count++;
&print_board;
&check_for_winners;
if ($count >= 9) {
print "IT'S A DRAW. THANK YOU.\n";
exit;
}
&player_choice;
$count++;
&print_board;
&check_for_winners;
}
}
print "IT'S A DRAW. THANK YOU.\n";
exit;
}
# This will check to see if anyone has won by adding up the various 3-in-a-row lines.
sub check_for_winners {
my %tally;
foreach my $key (keys %winning_combos) {
foreach my $val (@{$winning_combos{$key}}) {
$tally{$key}+=$board{$val};
}
}
foreach my $key (keys %tally) {
if ($tally{$key} == $player_goal) {
print "YOU BEAT ME!! GOOD GAME.\n";
exit;
}
if ($tally{$key} == $computer_goal) {
print "I WIN, TURKEY!!!\n";
exit;
}
}
}
#On the computer's turn it will first check to see if it should block the player. If it finds it isn't going to win or need to block a player, the it will choose a spot to place it's X or O.
sub computer_choice {
my $move;
$move=&check_for_blocks_or_wins;;
if ($move > 9) {
$move=&check_for_corners;
}
print "THE COMPUTER MOVES TO...\n";
$board{$move}=$computer;
}
sub check_for_corners {
my @precedence;
if ($count == 0) {
@precedence=(1,9,7,3,5,2,4,6,8);
}
else {
@precedence=(5,1,9,7,3,2,4,6,8);
}
foreach my $move (@precedence) {
my $validity=&check_occupation($move);
if ($validity eq "valid") {
return $move;
}
}
}
sub check_for_blocks_or_wins {
my %tally;
my $validity = "invalid";
my $move = 10;
foreach my $key (keys %winning_combos) {
foreach my $val (@{$winning_combos{$key}}) {
$tally{$key}+=$board{$val};
}
}
foreach my $key (keys %tally) {
if (abs($tally{$key}) == 2) {
until ($validity eq "valid") {
foreach my $val (@{$winning_combos{$key}}) {
$validity=&check_occupation($val);
if ($validity eq "valid") {
$move = $val;
last;
}
}
}
return $move;
}
}
return $move;
}
sub player_choice {
my $validity = "invalid";
my $ans = "";
until ($validity eq "valid") {
print "WHERE DO YOU MOVE? ";
chomp($ans = <STDIN>);
$validity=&check_occupation($ans);
if ($validity eq "invalid") {print "THAT SQUARE IS OCCUPIED.\n\n"}
}
$board{$ans}=$player;
}
sub check_occupation {
my $space = shift;
if ($board{$space}==0) { return "valid" }
else {return "invalid"};
}
sub print_board {
foreach my $num (1..9) {
my $char = &which_char($board{$num});
if ($num == 4 || $num == 7) { print "\n---+---+---\n";}
print "$char";
if ($num % 3 > 0) { print "!" }
}
print "\n";
}
sub which_char {
my $val=shift;
if ($val == 0) {return " ";}
elsif ($val == 1) {return " X ";}
else {return " O ";}
}
sub assign_X_and_O {
my $ans = shift;
if ($ans eq "X") {
$player = 1;
$computer = -1;
$player_goal=3;
$computer_goal=-3;
}
else {
$player = -1;
$computer = 1;
$player_goal=-3;
$computer_goal=3;
}
}
sub print_intro {
print ' ' x 30 . "TIC-TAC-TOE\n";
print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
print "THE BOARD IS NUMBERED:\n";
print "1 2 3\n4 5 6\n7 8 9\n\n\n";
}

119
92_Trap/csharp/Program.cs Normal file
View File

@@ -0,0 +1,119 @@
using System;
namespace trap_cs
{
class Program
{
const int maxGuesses = 6;
const int maxNumber = 100;
static void Main(string[] args)
{
int lowGuess = 0;
int highGuess = 0;
Random randomNumberGenerator = new ();
Print("TRAP");
Print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Print();
Print();
Print();
PrintInstructions();
int numberToGuess = randomNumberGenerator.Next(1, maxNumber);
for (int nGuess = 1; nGuess <= maxGuesses + 1; nGuess++)
{
if (nGuess > maxGuesses)
{
Print(string.Format("SORRY, THAT'S {0} GUESSES. THE NUMBER WAS {1}", maxGuesses, numberToGuess));
Print();
break;
}
GetGuesses(nGuess, ref lowGuess, ref highGuess);
if(lowGuess == highGuess && lowGuess == numberToGuess)
{
Print("YOU GOT IT!!!");
Print();
Print("TRY AGAIN.");
Print();
break;
}
if (highGuess < numberToGuess)
{
Print("MY NUMBER IS LARGER THAN YOUR TRAP NUMBERS.");
}
else if (lowGuess > numberToGuess)
{
Print("MY NUMBER IS SMALLER THAN YOUR TRAP NUMBERS.");
}
else
{
Print("YOU HAVE TRAPPED MY NUMBER.");
}
}
}
// TRAP
// REM - STEVE ULLMAN, 8 - 1 - 72
static void PrintInstructions()
{
Print("INSTRUCTIONS ?");
char response = Console.ReadKey().KeyChar;
if (response == 'Y')
{
Print(string.Format("I AM THINKING OF A NUMBER BETWEEN 1 AND {0}", maxNumber));
Print("TRY TO GUESS MY NUMBER. ON EACH GUESS,");
Print("YOU ARE TO ENTER 2 NUMBERS, TRYING TO TRAP");
Print("MY NUMBER BETWEEN THE TWO NUMBERS. I WILL");
Print("TELL YOU IF YOU HAVE TRAPPED MY NUMBER, IF MY");
Print("NUMBER IS LARGER THAN YOUR TWO NUMBERS, OR IF");
Print("MY NUMBER IS SMALLER THAN YOUR TWO NUMBERS.");
Print("IF YOU WANT TO GUESS ONE SINGLE NUMBER, TYPE");
Print("YOUR GUESS FOR BOTH YOUR TRAP NUMBERS.");
Print(string.Format("YOU GET {0} GUESSES TO GET MY NUMBER.", maxGuesses));
}
}
static void Print(string stringToPrint)
{
Console.WriteLine(stringToPrint);
}
static void Print()
{
Console.WriteLine();
}
static void GetGuesses(int nGuess, ref int lowGuess, ref int highGuess)
{
Print();
Print(string.Format("GUESS #{0}", nGuess));
lowGuess = GetIntFromConsole("Type low guess");
highGuess = GetIntFromConsole("Type high guess");
if(lowGuess > highGuess)
{
int tempGuess = lowGuess;
lowGuess = highGuess;
highGuess = tempGuess;
}
}
static int GetIntFromConsole(string prompt)
{
Console.Write( prompt + " > ");
string intAsString = Console.ReadLine();
if(int.TryParse(intAsString, out int intValue) ==false)
{
intValue = 1;
}
return intValue;
}
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>_23matches</RootNamespace>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32002.261
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "23matches", "23matches.csproj", "{9DBE7354-0749-4750-9224-5F9F95C64905}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9DBE7354-0749-4750-9224-5F9F95C64905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DBE7354-0749-4750-9224-5F9F95C64905}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DBE7354-0749-4750-9224-5F9F95C64905}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DBE7354-0749-4750-9224-5F9F95C64905}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0A87AE2F-68AC-4354-9C8D-578209D41174}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,129 @@
using System;
using System.Threading;
namespace _23matches
{
class Program
{
/// <summary>
/// Mimics the "goto" version of the original program
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Random random = new Random();
StartNewGame:
Console.WriteLine("23 MATCHES".PadLeft(31));
Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".PadLeft(15));
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("THIS IS A GAME CALLED '23 MATCHES'.");
Console.WriteLine();
Console.WriteLine("WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE");
Console.WriteLine("MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE");
Console.WriteLine("THE LAST MATCH.");
Console.WriteLine();
Console.WriteLine("Input exit to close the program.");
Console.WriteLine("Input cls Screen Clear.");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("LET'S FLIP A COIN TO SEE WHO GOES FIRST.");
Console.WriteLine("IF IT COMES UP HEADS, I WILL WIN THE TOSS.");
Console.WriteLine();
StartTheGame:
string command;
int N = 23;
int K = 0;
int Q = random.Next(2);
if (Q == 1)
goto ComputerFirst;
else
goto PlayerFirst;
ComputerFirst:
Console.WriteLine("HEADS! I WIN! HA! HA!");
Console.WriteLine("PREPARE TO LOSE, MEATBALL-NOSE!!");
Console.WriteLine();
int ain = random.Next(1, 3);
Console.WriteLine($"I TAKE {ain} MATCHES");
N = N - ain;
goto PlayersProceed;
PlayerFirst:
Console.WriteLine("TAILS! YOU GO FIRST. ");
Console.WriteLine();
goto PlayersSpeak;
PlayersProceed:
Console.WriteLine($"THE NUMBER OF MATCHES IS NOW {N}");
Console.WriteLine();
Console.WriteLine("YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.");
Console.WriteLine("HOW MANY DO YOU WISH TO REMOVE ");
goto PlayersSpeak;
PlayersSpeak:
command = Console.ReadLine().ToLower();
if (command.Equals("exit"))
{
System.Diagnostics.Process tt = System.Diagnostics.Process.GetProcessById(System.Diagnostics.Process.GetCurrentProcess().Id);
tt.Kill();
}
if (command.Equals("cls"))
{
Console.Clear();
goto PlayersProceed;
}
try
{
K = Convert.ToInt32(command);
}
catch (System.Exception)
{
goto PlayerInputError;
}
if (K > 3 || K <= 0)
goto PlayerInputError;
N = N - K;
Console.WriteLine($"THERE ARE NOW {N} MATCHES REMAINING.");
if (N == 4 || N == 3 || N == 2)
goto TheComputerSpeaks;
else if (N <= 1)
goto ThePlayerWins;
else
goto TheComputerSpeaks;
TheComputerSpeaks:
int Z = 4 - K;
Console.WriteLine($"MY TURN ! I REMOVE {Z} MATCHES");
N = N - Z;
if (N <= 1)
goto TheComputerWins;
else
goto PlayersProceed;
PlayerInputError:
Console.WriteLine("VERY FUNNY! DUMMY!");
Console.WriteLine("DO YOU WANT TO PLAY OR GOOF AROUND?");
Console.WriteLine("NOW, HOW MANY MATCHES DO YOU WANT ");
goto PlayersSpeak;
ThePlayerWins:
Console.WriteLine("YOU WON, FLOPPY EARS !");
Console.WriteLine("THINK YOU'RE PRETTY SMART !");
Console.WriteLine("LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!");
Console.WriteLine();
Console.WriteLine();
goto StartTheGame;
TheComputerWins:
Console.WriteLine();
Console.WriteLine("YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!");
Console.WriteLine("HA ! HA ! I BEAT YOU !!!");
Console.WriteLine();
Console.WriteLine("GOOD BYE LOSER!");
Console.WriteLine();
Console.WriteLine();
goto StartNewGame;
}
}
}

Some files were not shown because too many files have changed in this diff Show More