Merge branch 'coding-horror:main' into main

This commit is contained in:
Paul Holt
2022-01-04 10:15:39 +11:00
committed by GitHub
222 changed files with 8438 additions and 509 deletions

View File

@@ -1,7 +1,16 @@
### Acey Ducey
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=2
This is a simulation of the Acey Ducey card game. In the game, the dealer (the computer) deals two cards face up. You have an option to bet or not to bet depending on whether or not you feel the next card dealt will have a value between the first two.
Your initial money is set to $100; you may want to alter this value if you want to start with more or less than $100. The game keeps going on until you lose all your money or interrupt the program.
The original program author was Bill Palmby of Prairie View, Illinois.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=2)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=17)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true
}

View File

@@ -1,9 +1,7 @@
<html>
<head>
<!DOCTYPE html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>ACEY DUCEY</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<pre id="output" style="font-size: 12pt"></pre>
<script src="aceyducey.js"></script>
</body>
</html>

View File

@@ -1,135 +1,216 @@
// ACEY DUCEY
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
// UTILITY VARIABLES
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
// By default:
// — Browsers have a window object
// — Node.js does not
// Checking for an undefined window object is a loose check
// to enable browser and Node.js support
const isRunningInBrowser = typeof window !== 'undefined';
// To easily validate input strings with utility functions
const validLowerCaseYesStrings = ['yes', 'y'];
const validLowerCaseNoStrings = ['no', 'n'];
const validLowerCaseYesAndNoStrings = [
...validLowerCaseYesStrings,
...validLowerCaseNoStrings,
];
// UTILITY VARIABLES
// Function to get a random number (card) 2-14 (ACE is 14)
function getRandomCard() {
// In our game, the value of ACE is greater than face cards;
// instead of having the value of ACE be 1, well have it be 14.
// So, we want to shift the range of random numbers from 1-13 to 2-14
let min = 2;
let max = 14;
// Return random integer between two values, inclusive
return Math.floor(Math.random() * (max - min + 1) + min);
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
function newGameCards() {
let cardOne = getRandomCard();
let cardTwo = getRandomCard();
let cardThree = getRandomCard();
// We want:
// 1. cardOne and cardTwo to be different cards
// 2. cardOne to be lower than cardTwo
// So, while cardOne is greater than or equal too cardTwo
// we will continue to generate random cards.
while (cardOne >= cardTwo) {
cardOne = getRandomCard();
cardTwo = getRandomCard();
}
});
});
return [cardOne, cardTwo, cardThree];
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
// Function to get card value
function getCardValue(card) {
let faceOrAce = {
11: 'JACK',
12: 'QUEEN',
13: 'KING',
14: 'ACE',
};
// If card value matches a key in faceOrAce, use faceOrAce value;
// Else, return undefined and handle with the Nullish Coalescing Operator (??)
// and default to card value.
let cardValue = faceOrAce[card] ?? card;
return cardValue;
}
print(tab(26) + "ACEY DUCEY CARD GAME\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER\n");
print("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\n");
print("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n");
print("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\n");
print("A VALUE BETWEEN THE FIRST TWO.\n");
print("IF YOU DO NOT WANT TO BET, INPUT A 0\n");
function show_card(card)
{
if (card < 11)
print(card + "\n");
else if (card == 11)
print("JACK\n");
else if (card == 12)
print("QUEEN\n");
else if (card == 13)
print("KING\n");
else
print("ACE\n");
}
// Main program
async function main()
{
q = 100;
while (1) {
print("YOU NOW HAVE " + q + " DOLLARS.\n");
print("\n");
do {
print("HERE ARE YOUR NEXT TWO CARDS: \n");
do {
a = Math.floor(Math.random() * 13 + 2);
b = Math.floor(Math.random() * 13 + 2);
} while (a >= b) ;
show_card(a);
show_card(b);
print("\n");
while (1) {
print("\n");
print("WHAT IS YOUR BET");
m = parseInt(await input());
if (m > 0) {
if (m > q) {
print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
print("YOU HAVE ONLY " + q + "DOLLARS TO BET.\n");
continue;
}
break;
}
m = 0;
print("CHICKEN!!\n");
print("\n");
break;
}
} while (m == 0) ;
c = Math.floor(Math.random() * 13 + 2);
show_card(c);
if (c > a && c < b) {
print("YOU WIN!!!\n");
q = q + m;
} else {
print("SORRY, YOU LOSE\n");
if (m >= q) {
print("\n");
print("\n");
print("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n");
print("\n");
print("\n");
print("TRY AGAIN (YES OR NO)");
a = await input();
print("\n");
print("\n");
if (a == "YES") {
q = 100;
} else {
print("O.K., HOPE YOU HAD FUN!");
break;
}
} else {
q = q - m;
}
}
}
}
print(spaces(26) + 'ACEY DUCEY CARD GAME');
print(spaces(15) + 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n');
print('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER');
print('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
print('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
print('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
print('A VALUE BETWEEN THE FIRST TWO.');
print("IF YOU DO NOT WANT TO BET, INPUT '0'");
main();
async function main() {
let bet;
let availableDollars = 100;
// Loop game forever
while (true) {
let [cardOne, cardTwo, cardThree] = newGameCards();
print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
print('HERE ARE YOUR NEXT TWO CARDS: ');
print(getCardValue(cardOne));
print(getCardValue(cardTwo));
print('');
// Loop until receiving a valid bet
let validBet = false;
while (!validBet) {
print('\nWHAT IS YOUR BET? ');
bet = parseInt(await input(), 10);
let minimumRequiredBet = 0;
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('');
}
}
print('\n\nHERE IS THE CARD WE DREW: ');
print(getCardValue(cardThree));
// Determine if player won or lost
if (cardThree > cardOne && cardThree < cardTwo) {
print('YOU WIN!!!');
availableDollars = availableDollars + bet;
} else {
print('SORRY, YOU LOSE');
if (bet >= availableDollars) {
print('');
print('');
print('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
print('');
print('');
print('TRY AGAIN (YES OR NO)');
let tryAgainInput = await input();
print('');
print('');
if (isValidYesNoString(tryAgainInput)) {
availableDollars = 100;
} else {
print('O.K., HOPE YOU HAD FUN!');
break;
}
} else {
availableDollars = availableDollars - bet;
}
}
}
}
// UTILITY FUNCTIONS
function isValidYesNoString(string) {
return validLowerCaseYesAndNoStrings.includes(string.toLowerCase());
}
function isValidYesString(string) {
return validLowerCaseYesStrings.includes(string.toLowerCase());
}
function isValidNoString(string) {
return validLowerCaseNoStrings.includes(string.toLowerCase());
}
function print(string) {
if (isRunningInBrowser) {
// Adds trailing newline to match console.log behavior
document
.getElementById('output')
.appendChild(document.createTextNode(string + '\n'));
} else {
console.log(string);
}
}
function input() {
if (isRunningInBrowser) {
// Accept input from the browser DOM input
return new Promise((resolve) => {
const outputElement = document.querySelector('#output');
const inputElement = document.createElement('input');
outputElement.append(inputElement);
inputElement.focus();
inputElement.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
const result = inputElement.value;
inputElement.remove();
print(result);
print('');
resolve(result);
}
});
});
} else {
// Accept input from the command line in Node.js
// See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
return new Promise(function (resolve) {
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout,
});
readline.question('', function (input) {
resolve(input);
readline.close();
});
});
}
}
function printInline(string) {
if (isRunningInBrowser) {
document
.getElementById('output')
.appendChild(document.createTextNode(string));
} else {
process.stdout.write(string);
}
}
function spaces(numberOfSpaces) {
return ' '.repeat(numberOfSpaces);
}
// UTILITY FUNCTIONS

View File

@@ -85,6 +85,7 @@ end;
constructor TGame.Create;
begin
Randomize;
FDeck:= TDeck.Create;
end;
@@ -99,7 +100,6 @@ begin
ClrScr;
PrintGreeting;
repeat
Randomize;
FStash:= 100;
repeat
PrintBalance;

View File

@@ -118,10 +118,10 @@ begin
end;
begin
Randomize;
ClrScr;
PrintGreeting;
repeat
Randomize;
Stash:= 100;
repeat
PrintBalance;

View File

@@ -78,7 +78,7 @@ if __name__ == "__main__":
"""
Acey-Ducey is played in the following manner
The dealer (computer) deals two cards face up
You have an option to be or not bet depending
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

View File

@@ -0,0 +1,128 @@
#
# AceyDuchy
#
# From: BASIC Computer Games (1978)
# Edited by David Ahl
#
# "The original BASIC program author was Bill Palmby
# of Prairie View, Illinois."
#
# Python port by Aviyam Fischer, 2022
#
######################################################
class Card:
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
def __str__(self):
r = self.rank
if r == 11:
r = 'J'
elif r == 12:
r = 'Q'
elif r == 13:
r = 'K'
elif r == 14:
r = 'A'
return f'{r}{self.suit}'
class Deck:
def __init__(self):
self.cards = []
self.build()
def build(self):
for suit in ['\u2665', '\u2666', '\u2663', '\u2660']:
for rank in range(1, 14):
self.cards.append(Card(suit, rank))
def shuffle(self):
import random
random.shuffle(self.cards)
def deal(self):
return self.cards.pop()
class Game:
def __init__(self):
self.deck = Deck()
self.deck.shuffle()
self.card_a = self.deck.deal()
self.card_b = self.deck.deal()
self.money = 100
self.not_done = True
def play(self):
while self.not_done:
while self.money > 0:
card_a = self.card_a
card_b = self.card_b
if card_a.rank > card_b.rank:
card_a, card_b = card_b, card_a
if card_a.rank == card_b.rank:
self.card_b = self.deck.deal()
card_b = self.card_b
print(f'You have:\t ${self.money} ')
print(f'Your cards:\t {card_a} {card_b}')
bet = int(input('What is your bet? '))
player_card = self.deck.deal()
if 0 < bet <= self.money:
print(f'Your deal:\t {player_card}')
if card_a.rank < player_card.rank < card_b.rank:
print('You Win!')
self.money += bet
else:
print('You Lose!')
self.money -= bet
self.not_done = False
else:
print('Chicken!')
print(f'Your deal should have been: {player_card}')
if card_a.rank < player_card.rank < card_b.rank:
print(f'You could have won!')
else:
print(f'You would lose, so it was wise of you to chicken out!')
self.not_done = False
break
if len(self.deck.cards) <= 3:
print('You ran out of cards. Game over.')
self.not_done = False
break
self.card_a = self.deck.deal()
self.card_b = self.deck.deal()
if self.money == 0:
self.not_done = False
if __name__ == '__main__':
print('''
Acey Ducey is a card game where you play against the computer.
The Dealer(computer) will deal two cards facing 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
''')
GAME_OVER = False
while not GAME_OVER:
game = Game()
game.play()
print(f'You have ${game.money} left')
print('Would you like to play again? (y/n)')
if input() == 'n':
GAME_OVER = True
print('\nThanks for playing!')

View File

@@ -174,7 +174,7 @@ print("OK Hope you had fun\n")
#
# Give the user the ability to quit the game, perhaps
# by typing "quit" instead of making a bet. Provide a
# final assement based on how much of the original
# final assessment based on how much of the original
# bankroll they have left.
#
# Or have the game run for a set number of rounds or

View File

@@ -56,9 +56,9 @@ while true # Game loop
puts
puts "HERE ARE YOUR NEXT TWO CARDS:"
# Randomly draw two cards from 2 to 14 and make sure the first card is lower in value than the second
# Randomly draw two cards and make sure the first card is lower in value than the second
# Using array destructuring, this sorted array can be assigned to `first_card` and `second_card`
first_card, second_card = [rand(2..14), rand(2..14)].sort
first_card, second_card = (2...14).to_a.shuffle.pop(2).sort
# Helper method to convert a numeric card into a String for printing
def card_name(card)

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "AceyDucy", "AceyDucy\AceyDucy.vbproj", "{37496710-B458-4502-ADCB-4C57203866F9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C01D9DAE-644C-455F-8365-E14E49074BC3}
EndGlobalSection
EndGlobal

View File

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

View File

@@ -0,0 +1,178 @@
Imports System
''' <summary>
''' This is a modern adapation of Acey Ducey from BASIC Computer Games.
'''
''' The structural changes primarily consist of replacing the many GOTOs with
''' Do/Loop constructs to force the continual execution of the program.
'''
''' Because modern Basic allows multi-line If/Then blocks, many GOTO jumps were
''' able to be eliminated and the logic was able to be moved to more relevant areas,
''' For example, the increment/decrement of the player's balance could be in the same
''' area as the notification of win/loss.
'''
''' Some modern improvements were added, primarily the inclusion of a function, which
''' eliminated a thrice-repeated block of logic to display the card value. The archaic
''' RND function is greatly simplified with the .NET Framework's Random class.
'''
''' Elementary comments are provided for non-programmers or novices.
''' </summary>
Module Program
Sub Main(args As String())
' These are the variables that will hold values during the program's execution
Dim input As String
Dim rnd As New Random ' You can create a new instance of an object during declaration
Dim currentBalance As Integer = 100 ' You can set a initial value at declaration
Dim currentWager As Integer
Dim cardA, cardB, cardC As Integer ' You can specify multiple variables of the same type in one declaration statement
' Display the opening title and instructions
' Use a preceding $ to insert calculated values within the string using {}
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
Console.WriteLine("")
Console.WriteLine("")
Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
Do ' This loop continues as long as the player wants to keep playing
Do ' This loop continues as long as the player has money to play
Console.WriteLine("")
Console.WriteLine($"YOU NOW HAVE {currentBalance} DOLLARS.")
Console.WriteLine("")
Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS:")
' We need to ensure that card B is a higher value for our later comparison,
' so we will loop until we have two cards that meet this criteria
Do
cardA = rnd.Next(2, 14)
cardB = rnd.Next(2, 14)
Loop While cardA > cardB
' We use a function to display the text value of the numeric card value
' because we do this 3 times and a function reduces repetition of code
Console.WriteLine(DisplayCard(cardA))
Console.WriteLine(DisplayCard(cardB))
Do ' This loop continues until the player provides a valid wager value
Console.WriteLine("")
Console.WriteLine("WHAT IS YOUR BET")
currentWager = 0
input = Console.ReadLine
' Any input from the console is a string, but we require a number.
' Test the input to make sure it is a numeric value.
If Integer.TryParse(input, currentWager) Then
' Test to ensure the player has not wagered more than their balance
If currentWager > currentBalance Then
Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
Console.WriteLine($"YOU HAVE ONLY {currentBalance} DOLLARS TO BET.")
Else
' The player has provided a numeric value that is less/equal to their balance,
' exit the loop and continue play
Exit Do
End If ' check player balance
End If ' check numeric input
Loop ' wager loop
' If the player is wagering, draw the third card, otherwise, mock them.
If currentWager > 0 Then
cardC = rnd.Next(2, 14)
Console.WriteLine(DisplayCard(cardC))
' The effort we made to have two cards in numeric order earlier makes this check easier,
' otherwise we would have to have a second check in the opposite direction
If cardC < cardA OrElse cardC >= cardB Then
Console.WriteLine("SORRY, YOU LOSE")
currentBalance -= currentWager ' Shorthand code to decrement a number (currentBalance=currentBalance - currentWager)
Else
Console.WriteLine("YOU WIN!!!")
currentBalance += currentWager ' Shorthand code to increment a number (currentBalance=currentBalance + currentWager)
End If
Else
Console.WriteLine("CHICKEN!!")
Console.WriteLine("")
End If
Loop While currentBalance > 0 ' loop as long as the player has money
' At this point, the player has no money (currentBalance=0). Inform them of such.
Console.WriteLine("")
Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
Console.WriteLine("")
Console.WriteLine("")
' We will loop to ensure the player provides some answer.
Do
Console.WriteLine("TRY AGAIN (YES OR NO)")
Console.WriteLine("")
input = Console.ReadLine
Loop While String.IsNullOrWhiteSpace(input)
' We will assume that the player wants to play again only if they answer yes.
' (yeah and ya are valid as well, because we only check the first letter)
If input.Substring(0, 1).Equals("y", StringComparison.CurrentCultureIgnoreCase) Then ' This allows upper and lower case to be entered.
currentBalance = 100 ' Reset the players balance before restarting
Else
' Exit the outer loop which will end the game.
Exit Do
End If
Loop ' The full game loop
Console.WriteLine("O.K., HOPE YOU HAD FUN!")
End Sub
' This function is called for each of the 3 cards used in the game.
' The input and the output are both consistent, making it a good candidate for a function.
Private Function DisplayCard(value As Integer) As String
' We check the value of the input and run a block of code for whichever
' evaluation matches
Select Case value
Case 2 To 10 ' Case statements can be ranges of values, also multiple values (Case 2,3,4,5,6,7,8,9,10)
Return value.ToString
Case 11
Return "JACK"
Case 12
Return "QUEEN"
Case 13
Return "KING"
Case 14
Return "ACE"
End Select
' Although we have full knowledge of the program and never plan to send an invalid
' card value, it's important to provide a message for the next developer who won't
Throw New ArgumentOutOfRangeException("Card value must be between 2 and 14")
End Function
End Module

View File

@@ -1,7 +1,14 @@
### Amazing
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=3
This program will print out a different maze every time it is run and guarantees only one path through. You can choose the dimensions of the maze — i.e. the number of squares wide and long.
The original program author was Jack Hauber of Windsor, Connecticut.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=3)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=18)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,22 @@
### Animal
Unlike other computer games in which the computer picks a number or letter and you must guess what it is, in this game _you_ think of an animal and the _computer_ asks you questions and tries to guess the name of your animal. If the computer guesses incorrectly, it will ask you for a question that differentiates the animal you were thinking of. In this way the computer “learns” new animals. Questions to differentiate new animals should be input without a question mark.
This version of the game does not have a SAVE feature. If your system allows, you may modify the program to save and reload the array when you want to play the game again. This way you can save what the computer learns over a series of games.
At any time if you reply “LIST” to the question “ARE YOU THINKING OF AN ANIMAL,” the computer will tell you all the animals it knows so far.
The program starts originally by knowing only FISH and BIRD. As you build up a file of animals you should use broad, general questions first and then narrow down to more specific ones with later animals. For example, if an elephant was to be your first animal, the computer would ask for a question to distinguish an elephant from a bird. Naturally, there are hundreds of possibilities, however, if you plan to build a large file of animals a good question would be “IS IT A MAMMAL.”
This program can be easily modified to deal with categories of things other than animals by simply modifying the initial data and the dialogue references to animals. In an educational environment, this would be a valuable program to teach the distinguishing characteristics of many classes of objects — rock formations, geography, marine life, cell structures, etc.
Originally developed by Arthur Luehrmann at Dartmouth College, Animal was subsequently shortened and modified by Nathan Teichholtz at DEC and Steve North at Creative Computing.
---
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=4
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=4)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=19)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

119
03_Animal/kotlin/Animal.kt Normal file
View File

@@ -0,0 +1,119 @@
/**
* ANIMAL
*
*
* Converted from BASIC to Kotlin by John Long (@patimen)
*
* Animal is basically a perfect example of a binary tree. Implement it
* as such, with the QuestionNode either having an answer if it is a terminal node
* or a Question
*/
fun main() {
printIntro()
val rootQuestionNode =
QuestionOrAnswer(question = Question("DOES IT SWIM", QuestionOrAnswer("FISH"), QuestionOrAnswer("BIRD")))
while (true) {
val choice = ask("ARE YOU THINKING OF AN ANIMAL")
when {
choice == "LIST" -> printKnownAnimals(rootQuestionNode)
choice.startsWith("Q") -> return
choice.startsWith("Y") -> {
// A wrong answer means it's a new animal!
val wrongAnswer = rootQuestionNode.getWrongAnswer()
if (wrongAnswer == null) {
// The computer got the right answer!
println("WHY NOT TRY ANOTHER ANIMAL?")
} else {
// Get a new question to ask next time
wrongAnswer.askForInformationAndSave()
}
}
}
}
}
// Takes care of asking a question (on the same line) and getting
// an answer or a blank string
fun ask(question: String): String {
print("$question? ")
return readLine()?.uppercase() ?: ""
}
// Special case for a "yes or no" question, returns true of yes
fun askYesOrNo(question: String): Boolean {
return generateSequence {
print("$question? ")
readLine()
}.firstNotNullOf { yesOrNo(it) }
}
// If neither Y (true) or N (false), return null, so the above sequence
// will just keep executing until it gets the answer
private fun yesOrNo(string: String): Boolean? =
when (string.uppercase().firstOrNull()) {
'Y' -> true
'N' -> false
else -> null
}
private fun printKnownAnimals(question: QuestionOrAnswer) {
println("\nANIMALS I ALREADY KNOW ARE:")
val animals = question.getAnswers().chunked(4)
animals.forEach { line ->
// The '*' in front of line.toTypedArray() "spreads" the array as a list of parameters instead
System.out.printf("%-15s".repeat(line.size), *line.toTypedArray())
println()
}
}
private fun printIntro() {
println(" ANIMAL")
println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
println("\n\n")
println("PLAY 'GUESS THE ANIMAL'")
println("\n")
println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.")
}
class QuestionOrAnswer(private var answer: String? = null, var question: Question? = null) {
fun getAnswers(): List<String> = answer?.let { listOf(it) } ?: question!!.getAnswers()
fun getWrongAnswer(): QuestionOrAnswer? {
if (answer != null) {
// "takeUnless" will return null if the answer is "yes". In this case
// we will return the "wrong answer", aka the terminal answer that was incorrect
return this.takeUnless { askYesOrNo("IS IT A $answer") }
}
return question?.getWrongAnswer()
}
fun askForInformationAndSave() {
//Failed to get it right and ran out of questions
//Let's ask the user for the new information
val newAnimal = ask("THE ANIMAL YOU WERE THINKING OF WAS A")
val newQuestion = ask("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A \n$newAnimal FROM A $answer\n")
val newAnswer = askYesOrNo("FOR A $newAnimal THE ANSWER WOULD BE")
val trueAnswer = if (newAnswer) newAnimal else answer
val falseAnswer = if (newAnswer) answer else newAnimal
// Replace our answer with null and set the question with the data we just got
// This makes it a question instead of an answer
this.answer = null
this.question = Question(newQuestion, QuestionOrAnswer(trueAnswer), QuestionOrAnswer(falseAnswer))
}
}
class Question(
private val question: String,
private val trueAnswer: QuestionOrAnswer,
private val falseAnswer: QuestionOrAnswer
) {
fun getAnswers(): List<String> = trueAnswer.getAnswers() + falseAnswer.getAnswers()
fun getWrongAnswer(): QuestionOrAnswer? =
if (askYesOrNo(question)) {
trueAnswer.getWrongAnswer()
} else {
falseAnswer.getWrongAnswer()
}
}

View File

@@ -0,0 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Kotlin](https://kotlinlang.org/)

223
03_Animal/perl/animal.pl Executable file
View File

@@ -0,0 +1,223 @@
#!/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';
# The Perl ref() built-in returns 'HASH' for a hash reference. But we
# make it a manifest constant just to avoid typos.
use constant REF_HASH => ref {};
print <<'EOD';
ANIMAL
Creative Computing Morristown, New Jersey
Play 'Guess the Animal'
Think of an animal and the computer will try to guess it.
EOD
# We keep the accumulated data in a tree structure, initialized here. As
# we accumulate animals, we replace the 'yes' or 'no' keys with new hash
# references.
my $database = {
question => 'Does it swim', # Initial question
yes => 'fish', # Result of answering 'y'
no => 'bird', # Result of answering 'n'
};
while ( 1 ) {
my $resp = get_input(
'Are you thinking of an an animal? [y/n/list]: '
);
if ( $resp =~ m/ \A y /smxi ) {
# If we got an answer beginning with 'y', walk the database
walk_tree( $database );
} elsif ( $resp =~ m/ \A list \z /smxi ) {
# If we got 'list', list the currently-known animals.
say '';
say 'Animals I already know are:';
say " $_" for sort( list_animals( $database ) );
}
}
# 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( 'animal' );
$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 };
}
# Recurse through the database, returning the names of all animals in
# it, in an undefined order.
sub list_animals {
my ( $node ) = @ARG;
return $node unless REF_HASH eq ref $node;
return( map { list_animals( $node->{$_} ) } qw{ yes no } );
}
# Find or create the desired animal.
# Ask the question stored in the node given in its argument. If the key
# selected by the answer ('yes' or 'no') is another node, recurse. If it
# is an animal name, confirm it, or add a new animal as appropriate.
sub walk_tree {
my ( $node ) = @ARG;
# Ask the question associated with this node. Turn the true/false
# response into 'yes' or 'no', since those are the names of the
# respective keys.
my $resp = get_yes_no ( $node->{question} ) ? 'yes' : 'no';
# Chose the datum for the response.
my $choice = $node->{ $resp };
# If the datum is a hash reference
if ( REF_HASH eq ref $choice ) {
# Recurse into it
walk_tree( $choice );
# Otherwise it is an actual animal (i.e. terminal node). Check it.
} else {
# If this is not the animal the player was thinking of
unless ( get_yes_no( "Is it a $choice" ) ) {
# Find out what animal the player was thinking of
my $animal = lc get_input(
'The animal you were thinking of was a ',
);
# Get a yes/no question that distinguishes the animal the
# player was thinking of from the animal we found in the
# tree.
say 'Please type in a question that would distinguish a';
my $question = get_input( "$animal from a $choice: " );
# Find out whether the new animal is selected by 'yes' or
# 'no'. If 'no', swap the original animal with the new one
# for convenience.
( $choice, $animal ) = ( $animal, $choice ) if get_yes_no(
"For a $animal the answer would be",
);
# Replace the animal we originally found by a new node
# giving the original animal, the new animal, and the
# question that distinguishes them.
$node->{ $resp } = {
question => $question,
no => $animal,
yes => $choice,
};
}
# Find out if the player wants to play again. If not, exit. If
# so, just return.
say '';
exit unless get_yes_no( 'Why not try another animal' );
return;
}
}
__END__
=head1 TITLE
animal.pl - Play the game 'animal' from Basic Computer Games
=head1 SYNOPSIS
animal.pl
=head1 DETAILS
This Perl script is a port of C<animal>, which is the 3ed entry in Basic
Computer Games.
The original BASIC was greatly complicated by the need to emulate a
binary tree with an array. The implementation using hashes as nodes in
an actual binary tree is much simpler.
=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,7 +1,38 @@
### Awari
Awari is an ancient African game played with seven sticks and thirty-six stones or beans laid out as shown above. The board is divided into six compartments or pits on each side. In addition, there are two special home pits at the ends.
A move is made by taking all the beans from any (non-empty) pit on your own side. Starting from the pit to the right of this one, these beans are sown one in each pit working around the board anticlockwise.
A turn consists of one or two moves. If the last bean of your move is sown in your own home you may take a second move.
If the last bean sown in a move lands in an empty pit, provided that the opposite pit is not empty, all the beans in the opposite pit, together with the last bean sown are captured and moved to the players home.
When either side is empty, the game is finished. The player with the most beans in his home has won.
In the computer version, the board is printed as 14 numbers representing the 14 pits.
```
3 3 3 3 3 3
0 0
3 3 3 3 3 3
```
The pits on your (lower) side are numbered 1-6 from left to right. The pits on my (the computers) side are numbered from my left (your right).
To make a move you type in the number of a pit. If the last bean lands in your home, the computer types AGAIN? and then you type in your second move.
The computers move is typed, followed by a diagram of the board in its new state. The computer always offers you the first move. This is considered to be a slight advantage.
There is a learning mechanism in the program that causes the play of the computer to improve as it playes more games.
The original version of Awari is adopted from one originally written by Geoff Wyvill of Bradford, Yorkshire, England.
---
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=6
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=6)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=21)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

264
04_Awari/csharp/Game.cs Normal file
View File

@@ -0,0 +1,264 @@
namespace Awari;
public class Game
{
public int[] PlayerPits => _beans[0..6];
public int[] ComputerPits => _beans[7..13];
public int PlayerHome => _beans[_playerHome];
public int ComputerHome => _beans[_computerHome];
private bool IsDone =>
PlayerPits.All(b => b == 0) // if all the player's pits are empty
|| ComputerPits.All(b => b == 0); // or if all the computer's pits are empty
public GameState State { get; private set; }
public void Reset()
{
State = GameState.PlayerMove;
Array.Fill(_beans, _initialPitValue);
_beans[_playerHome] = 0;
_beans[_computerHome] = 0;
_moveCount = 0;
_notWonGameMoves[^1] = 0;
}
public bool IsLegalPlayerMove(int move) =>
move is > 0 and < 7
&& _beans[move - 1] > 0; // arrays are zero-based, but moves are one-based
public void PlayerMove(int move) => MoveAndRegister(move - 1, _playerHome);
public List<int> ComputerTurn()
{
// keep a list of moves made by the computer in a single turn (1 or 2)
List<int> moves = new();
moves.Add(ComputerMove()); // ComputerMove() returns the move made
// only if a second move is possible, do it
if (State == GameState.ComputerSecondMove)
moves.Add(ComputerMove());
return moves;
}
public GameOutcome GetOutcome()
{
if (State != GameState.Done)
throw new InvalidOperationException("Game is not yet done.");
int difference = _beans[_playerHome] - _beans[_computerHome];
var winner = difference switch
{
< 0 => GameWinner.Computer,
0 => GameWinner.Draw,
> 0 => GameWinner.Player,
};
return new GameOutcome(winner, Math.Abs(difference));
}
private void MoveAndRegister(int pit, int homePosition)
{
int lastMovedBean = Move(_beans, pit, homePosition);
// encode moves by player and computer into a 'base 6' number
// e.g. if the player moves 5, the computer moves 2, and the player moves 4,
// that would be encoded as ((5 * 6) * 6) + (2 * 6) + 4 = 196
if (pit > 6) pit -= 7;
_moveCount++;
if (_moveCount < 9)
_notWonGameMoves[^1] = _notWonGameMoves[^1] * 6 + pit;
// determine next state based on current state, whether the game's done, and whether the last moved bean moved
// into the player's home position
State = (State, IsDone, lastMovedBean == homePosition) switch
{
(_, true, _) => GameState.Done,
(GameState.PlayerMove, _, true) => GameState.PlayerSecondMove,
(GameState.PlayerMove, _, false) => GameState.ComputerMove,
(GameState.PlayerSecondMove, _, _) => GameState.ComputerMove,
(GameState.ComputerMove, _, true) => GameState.ComputerSecondMove,
(GameState.ComputerMove, _, false) => GameState.PlayerMove,
(GameState.ComputerSecondMove, _, _) => GameState.PlayerMove,
_ => throw new InvalidOperationException("Unexpected game state"),
};
// do some bookkeeping if the game is done, but not won by the computer
if (State == GameState.Done
&& _beans[_playerHome] >= _beans[_computerHome])
// add an entry for the next game
_notWonGameMoves.Add(0);
}
private static int Move(int[] beans, int pit, int homePosition)
{
int beansToMove = beans[pit];
beans[pit] = 0;
// add the beans that were in the pit to other pits, moving clockwise around the board
for (; beansToMove >= 1; beansToMove--)
{
// wrap around if pit exceeds 13
pit = (pit + 1) % 14;
beans[pit]++;
}
if (beans[pit] == 1 // if the last bean was sown in an empty pit
&& pit is not _playerHome and not _computerHome // which is not either player's home
&& beans[12 - pit] != 0) // and the pit opposite is not empty
{
// move the last pit sown and the _beans in the pit opposite to the player's home
beans[homePosition] = beans[homePosition] + beans[12 - pit] + 1;
beans[pit] = 0;
beans[12 - pit] = 0;
}
return pit;
}
private int ComputerMove()
{
int move = DetermineComputerMove();
MoveAndRegister(move, homePosition: _computerHome);
// the result is only used to return it to the application, so translate it from an array index (between 7 and
// 12) to a pit number (between 1 and 6)
return move - 6;
}
private int DetermineComputerMove()
{
int bestScore = -99;
int move = 0;
// for each of the computer's possible moves, simulate them to calculate a score and pick the best one
for (int j = 7; j < 13; j++)
{
if (_beans[j] <= 0)
continue;
int score = SimulateMove(j);
if (score >= bestScore)
{
move = j;
bestScore = score;
}
}
return move;
}
private int SimulateMove(int move)
{
// make a copy of the current state, so we can safely mess with it
var hypotheticalBeans = new int[14];
_beans.CopyTo(hypotheticalBeans, 0);
// simulate the move in our copy
Move(hypotheticalBeans, move, homePosition: _computerHome);
// determine the 'best' move the player could make after this (best for them, not for the computer)
int score = ScoreBestNextPlayerMove(hypotheticalBeans);
// score this move by calculating how far ahead we would be after the move, and subtracting the player's next
// move score
score = hypotheticalBeans[_computerHome] - hypotheticalBeans[_playerHome] - score;
// have we seen the current set of moves before in a drawn/lost game? after 8 moves it's unlikely we'll find any
// matches, since games will have diverged. also we don't have space to store that many moves.
if (_moveCount < 8)
{
int translatedMove = move - 7; // translate from 7 through 12 to 0 through 5
// if the first two moves in this game were 1 and 2, and this hypothetical third move would be a 3,
// movesSoFar would be (1 * 36) + (2 * 6) + 3 = 51
int movesSoFar = _notWonGameMoves[^1] * 6 + translatedMove;
// since we store moves as a 'base 6' number, we need to divide stored moves by a power of 6
// let's say we've a stored lost game where the moves were, in succession, 1 through 8, the value stored
// would be:
// 8 + (7 * 6) + (6 * 36) + (5 * 216) + (4 * 1296) + (3 * 7776) + (2 * 46656) + (1 * 279936) = 403106
// to figure out the first three moves, we'd need to divide by 7776, resulting in 51.839...
double divisor = Math.Pow(6.0, 7 - _moveCount);
foreach (int previousGameMoves in _notWonGameMoves)
// if this combination of moves so far ultimately resulted in a draw/loss, give it a lower score
// note that this can happen multiple times
if (movesSoFar == (int) (previousGameMoves / divisor + 0.1))
score -= 2;
}
return score;
}
private static int ScoreBestNextPlayerMove(int[] hypotheticalBeans)
{
int bestScore = 0;
for (int i = 0; i < 6; i++)
{
if (hypotheticalBeans[i] <= 0)
continue;
int score = ScoreNextPlayerMove(hypotheticalBeans, i);
if (score > bestScore)
bestScore = score;
}
return bestScore;
}
private static int ScoreNextPlayerMove(int[] hypotheticalBeans, int move)
{
// figure out where the last bean will land
int target = hypotheticalBeans[move] + move;
int score = 0;
// if it wraps around, that means the player is adding to his own pits, which is good
if (target > 13)
{
// prevent overrunning the number of pits we have
target %= 14;
score = 1;
}
// if the player's move ends up in an empty pit, add the value of the pit on the opposite side to the score
if (hypotheticalBeans[target] == 0 && target is not _playerHome and not _computerHome)
score += hypotheticalBeans[12 - target];
return score;
}
private const int _playerHome = 6;
private const int _computerHome = 13;
private const int _initialPitValue = 3;
private readonly int[] _beans = new int[14];
private readonly List<int> _notWonGameMoves = new() { 0 }; // not won means draw or lose
private int _moveCount;
}
public enum GameState
{
PlayerMove,
PlayerSecondMove,
ComputerMove,
ComputerSecondMove,
Done,
}
public enum GameWinner
{
Player,
Computer,
Draw,
}
public record struct GameOutcome(GameWinner Winner, int Difference);

View File

@@ -0,0 +1,98 @@
using Awari;
Console.WriteLine(Tab(34) + "AWARI");
Console.WriteLine(Tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Game game = new();
while (true)
{
game.Reset();
DisplayGame();
while (game.State != GameState.Done)
{
switch (game.State)
{
case GameState.PlayerMove:
PlayerMove(second: false);
break;
case GameState.PlayerSecondMove:
PlayerMove(second: true);
break;
case GameState.ComputerMove:
ComputerTurn();
break;
}
DisplayGame();
}
var outcome = game.GetOutcome();
string outcomeLabel =
outcome.Winner switch
{
GameWinner.Computer => $"I WIN BY {outcome.Difference} POINTS",
GameWinner.Draw => "DRAWN GAME",
GameWinner.Player => $"YOU WIN BY {outcome.Difference} POINTS",
_ => throw new InvalidOperationException($"Unexpected winner {outcome.Winner}."),
};
Console.WriteLine(outcomeLabel);
Console.WriteLine();
}
void DisplayGame()
{
// display the computer's pits
Console.Write(" ");
foreach (var pit in game.ComputerPits.Reverse())
Console.Write($"{pit,2} ");
Console.WriteLine();
// display both homes
Console.WriteLine($"{game.ComputerHome,2}{Tab(19)}{game.PlayerHome,2}");
// display the player's pits
Console.Write(" ");
foreach (var pit in game.PlayerPits)
Console.Write($"{pit,2} ");
Console.WriteLine();
Console.WriteLine();
}
void PlayerMove(bool second = false)
{
int move = GetMove(second);
game.PlayerMove(move);
}
int GetMove(bool second)
{
string prompt = second ? "AGAIN? " : "YOUR MOVE? ";
while (true)
{
Console.Write(prompt);
string input = Console.ReadLine() ?? "";
// input must be a number between 1 and 6, and the pit must have > 0 beans
if (int.TryParse(input, out int move)
&& game.IsLegalPlayerMove(move))
return move;
Console.WriteLine("ILLEGAL MOVE");
}
}
void ComputerTurn()
{
var moves = game.ComputerTurn();
string movesString = string.Join(",", moves);
Console.WriteLine($"MY MOVE IS {movesString}");
}
string Tab(int n) => new(' ', n);

View File

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

View File

@@ -1,7 +1,20 @@
### Bagels
In this game, the computer picks a 3-digit secret number using the digits 0 to 9 and you attempt to guess what it is. You are allowed up to twenty guesses. No digit is repeated. After each guess the computer will give you clues about your guess as follows:
- PICO One digit is correct, but in the wrong place
- FERMI One digit is in the correct place
- BAGELS No digit is correct
You will learn to draw inferences from the clues and, with practice, youll learn to improve your score. There are several good strategies for playing Bagels. After you have found a good strategy, see if you can improve it. Or try a different strategy altogether to see if it is any better. While the program allows up to twenty guesses, if you use a good strategy it should not take more than eight guesses to get any number.
The original authors of this program are D. Resek and P. Rowe of the Lawrence Hall of Science, Berkeley, California.
---
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=9
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=9)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=21)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

195
05_Bagels/perl/bagels.pl Executable file
View File

@@ -0,0 +1,195 @@
#!/usr/bin/perl
use strict;
use warnings;
# global variable declaration (just the user's score)
my($Y) = 0;
# yes_input is a subroutine that returns a true value
# if the first character of the user's input from STDIN
# is a 'Y' (checking case-insensitively via regex)
sub yes_input {
chomp(my $A = <STDIN>);
return $A =~ m/^Y/i;
}
# Main code starts here.
print ' 'x32; print "Bagels\n";
print ' 'x14; print "Creative Computing Morristown, New Jersey\n\n";
# Provide instructions if requested
print "Would you like the rules (yes or no)? ";
if (yes_input()) {
# Print out the instructions using a here doc
# (useful for large blocks of text)
print <<HERE;
I am thinking of a three-digit number. Try to guess
my number and I will give you clues as follows:
PICO - one digit correct but in the wrong position
FERMI - one digit correct and in the right place
BAGELS - no digits correct
HERE
}
# There are three code loops here. The outermost one, labeled 'PLAY',
# performs one game start to finish every time it runs.
# The next loop is a for loop that implements the required 20 guesses.
# And the innermost loop, labeled 'CHECK', does the game logic of
# checking the user's guess to make sure it's valid, figuring out
# the response, and rewarding the user if they won.
PLAY: while (1) {
# The computer's number is three randomly selected unique digits.
# To generate the number, 3 digits are randomly splice()d out
# of the @DIGITS array, which is initialized with the digits 0 to 9.
my @DIGITS = (0..9);
my @A = ();
push @A, splice(@DIGITS, int(rand(scalar @DIGITS)), 1) for 1..3;
print "\n";
print "O.K. I have a number in mind.\n";
for my $i (1..20) {
# Note that the CHECK loop will automatically loop to ask
# for the user's input, and it's only with the 'next' and
# 'last' statements that this loop is exited. So if the
# user's input is somehow invalid, we just print out an
# appropriate message. Execution will by default return
# to the start of the loop, and only leaves the loop if
# the user's input is successfully parsed and accepted
# (look for the 'next' and 'last' statements below).
CHECK: while (1) {
printf("Guess # %2d ? ", $i);
chomp(my $A = <STDIN>);
# Use a regex to check if the user entered three digits,
# and complain if they did not.
if ($A !~ m{^(\d)(\d)(\d)$}) {
print "What?\n";
# Program execution will now pass through the rest
# of the logic below and loop back to the start
# of the CHECK loop.
} else {
# As a side effect of the regex match above, the
# $1, $2, and $3 variables are each of the digits
# of the user's guess. Perl treats numbers and
# strings interchangably, so we will not have to
# use the ASC() conversion functions required
# by the BASIC program.
my @B = ($1,$2,$3);
# Check for duplicate digits in the user's guess
if ($B[0] == $B[1] || $B[0] == $B[2] || $B[1] == $B[2]) {
print "Oh, I forgot to tell you that the number I have in mind\n";
print "has no two digits the same.\n";
# Again, no further action is required here
# because we want to loop back to the start
# of the CHECK loop.
} else {
# This code block is the actual game logic, so
# it's executed only if the user's input has
# passed all the above checks.
my($C,$D);
$C = 0; $D = 0;
# As a replacement for the original BASIC logic,
# this for loop works over an anonymous array of
# pairs of digits to compare the computer's and
# the user's digits to see how many similar ones
# there are. Keep in mind that Perl arrays are
# zero-indexed, so we're comparing items numbered
# 0, 1, and 2, instead of 1, 2, and 3 in BASIC.
for my $PAIR ( [0,1], [1,0], [1,2], [2,1], [0,2], [2,0] ) {
if ($A[$PAIR->[0]] == $B[$PAIR->[1]]) {
++$C;
}
}
# Check for digits that are correctly guessed
for my $i (0..2) {
if ($A[$i] == $B[$i]) {
++$D;
}
}
# If the user guessed all 3 digits they get
# a point, and the 'PLAY' loop is restarted
# (see the 'continue' loop below)
if ($D == 3) {
print "You got it!!!\n\n";
++$Y;
next PLAY;
}
# Print out the clues. The 'x' operator
# prints out the string the indicated number
# of times. The "BAGELS" line uses Perl's
# ternary operator to print the word if
# the expression ($C + $D) is equal to 0.
printf("%s%s%s\n",
"PICO " x$C,
"FERMI "x$D,
($C+$D==0 ? "BAGELS" : '')
);
# Program execution leaves the CHECK loop and
# goes to the next iteration of the $i loop.
last CHECK;
} # end of game logic else block
} # end of regex match else block
# If program execution reaches this particular point,
# then the user's input has not been accepted (the
# only ways out of this loop are the "next PLAY" statement
# when the user wins, and the "last CHECK" statement
# when the user's input is successfully parsed).
# So the program execution goes back to the top of the
# CHECK loop, printing the request for user input
# again.
} # end of CHECK loop
# This location is reached by the "last CHECK" statement,
# and it's another execution of the $i loop.
} # end of $i loop
# If program execution reaches here, the user has guessed 20
# times and not won.
print "Oh well.\n";
printf("That's twenty guesses. My number was %s\n", join('',@A));
} # end of the PLAY loop
# This 'continue' block is executed before the conditional part of the
# PLAY loop is evaluated, so we can ask if the user wants another game
# (i.e., if we should restart the PLAY loop).
continue {
# This 'continue' loop is reached either when the PLAY loop has completed
# or via the 'next PLAY' statement when the user wins a game. In either
# case we ask if the player wants to go again, and use the 'last'
# statement to exit the loop if the response is not yes.
print "Play again (yes or no) ? ";
last unless yes_input();
}
# And as in the original BASIC program, print out
# the user's score only if it is > 0.
printf("A %d point bagels buff!\n", $Y) if $Y > 0;
print "Hope you had fun. Bye.\n";

View File

@@ -1,7 +1,14 @@
### Banner
This program creates a large banner on a terminal of any message you input. The letters may be any dimension of you wish although the letter height plus distance from left-hand side should not exceed 6 inches. Experiment with the height and width until you get a pleasing effect on whatever terminal you are using.
This program was written by Leonard Rosendust of Brooklyn, New York.
---
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=10
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=10)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=25)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,30 @@
### Basketball
This program simulates a game of basketball between Dartmouth College and an opponent of your choice. You are the Dartmouth captain and control the type of shot and defense during the course of the game.
There are four types of shots:
1. Long Jump Shot (30ft)
2. Short Jump Shot (15ft)
3. Lay Up
4. Set Shot
Both teams use the same defense, but you may call it:
- Enter (6): Press
- Enter (6.5): Man-to-man
- Enter (7): Zone
- Enter (7.5): None
To change defense, type “0” as your next shot.
Note: The game is biased slightly in favor of Dartmouth. The average probability of a Dartmouth shot being good is 62.95% compared to a probability of 61.85% for their opponent. (This makes the sample run slightly remarkable in that Cornell won by a score of 45 to 42 Hooray for the Big Red!)
Charles Bacheller of Dartmouth College was the original author of this game.
---
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=12
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=12)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=27)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,20 @@
### Batnum
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=14
The game starts with an imaginary pile of objects, coins for example. You and your opponent (the computer) alternately remove objects from the pile. You specify in advance the minimum and maximum number of objects that can be taken on each turn. You also specify in advance how winning is defined:
1. To take the last object
2. To avoid taking the last object
You may also determine whether you or the computer go first.
The strategy of this game is based on modulo arithmetic. If the maximum number of objects a player may remove in a turn is M, then to gain a winning position a player at the end of his turn must leave a stack of 1 modulo (M+1) coins. If you dont understand this, play the game 23 Matches first, then BATNUM, and have fun!
BATNUM is a generalized version of a great number of manual remove-the-object games. The original computer version was written by one of the two originators of the BASIC language, John Kemeny of Dartmouth College.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=14)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=29)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,27 @@
### Battle
BATTLE is based on the popular game Battleship which is primarily played to familiarize people with the location and designation of points on a coordinate plane.
BATTLE first randomly sets up the bad guys fleet disposition on a 6 by 6 matrix or grid. The fleet consists of six ships:
- Two destroyers (ships number 1 and 2) which are two units long
- Two cruisers (ships number 3 and 4) which are three units long
- Two aircraft carriers (ships number 5 and 6) which are four units long
The program then prints out this fleet disposition in a coded or disguised format (see the sample computer print-out). You then proceed to sink the various ships by typing in the coordinates (two digits. each from 1 to 6, separated by a comma) of the place where you want to drop a bomb, if youll excuse the expression. The computer gives the appropriate response (splash, hit, etc.) which you should record on a 6 by 6 matrix. You are thus building a representation of the actual fleet disposition which you will hopefully use to decode the coded fleet disposition printed out by the computer. Each time a ship is sunk, the computer prints out which ships have been sunk so far and also gives you a “SPLASH/HIT RATIO.”
The first thing you should learn is how to locate and designate positions on the matrix, and specifically the difference between “3,4” and “4,3.” Our method corresponds to the location of points on the coordinate plane rather than the location of numbers in a standard algebraic matrix: the first number gives the column counting from left to right and the second number gives the row counting from bottom to top.
The second thing you should learn about is the splash/hit ratio. “What is a ratio?” A good reply is “Its a fraction or quotient.” Specifically, the spash/hit ratio is the number of splashes divided by the number of hits. If you had 9 splashes and 15 hits, the ratio would be 9/15 or 3/5, both of which are correct. The computer would give this splash/hit ratio as .6.
The main objective and primary education benefit of BATTLE comes from attempting to decode the bas guys fleet disposition code. To do this, you must make a comparison between the coded matrix and the actual matrix which you construct as you play the game.
The original author of both the program and these descriptive notes is Ray Westergard of Lawrence Hall of Science, Berkeley, California.
---
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=15
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=15)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=30)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Blackjack
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=18
This is a simulation of the card game of Blackjack or 21, Las Vegas style. This rather comprehensive version allows for up to seven players. On each hand a player may get another card (a hit), stand, split a hand in the event two identical cards were received or double down. Also, the dealer will ask for an insurance bet if he has an exposed ace.
Cards are automatically reshuffled as the 51st card is reached. For greater realism, you may wish to change this to the 41st card. Actually, fanatical purists will want to modify the program so it uses three decks of cards instead of just one.
This program originally surfaced at Digital Equipment Corp.; the author is unknown.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=18)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=33)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Bombardment
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=22
BOMBARDMENT is played on two, 5x5 grids or boards with 25 outpost locations numbered 1 to 25. Both you and the computer have four platoons of troops that can be located at any four outposts on your respective grids.
At the start of the game, you locate (or hide) your four platoons on your grid. The computer does the same on its grid. You then take turns firing missiles or bombs at each others outposts trying to destroy all four platoons. The one who finds all four opponents platoons first, wins.
This program was slightly modified from the original written by Martin Burdash of Parlin, New Jersey.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=22)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=37)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,14 @@
### Bombs Away
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=24
In this program, you fly a World War II bomber for one of the four protagonists of the war. You then pick your target or the type of plane you are flying. Depending on your flying experience and the quality of enemy defenders, you then may accomplish your mission, get shot down, or make it back through enemy fire. In any case, you get a chance to fly again.
David Ahl modified the original program which was created by David Sherman while a student at Curtis Jr. High School, Sudbury, Massachusetts.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=24)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=39)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,31 @@

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}") = "BombsAwayConsole", "BombsAwayConsole\BombsAwayConsole.csproj", "{D80015FA-423C-4A16-AA2B-16669245AD59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BombsAwayGame", "BombsAwayGame\BombsAwayGame.csproj", "{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D80015FA-423C-4A16-AA2B-16669245AD59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D80015FA-423C-4A16-AA2B-16669245AD59}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D80015FA-423C-4A16-AA2B-16669245AD59}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D80015FA-423C-4A16-AA2B-16669245AD59}.Release|Any CPU.Build.0 = Release|Any CPU
{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {39B2ECFB-037D-4335-BBD2-64892E953DD4}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\BombsAwayGame\BombsAwayGame.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,134 @@
namespace BombsAwayConsole;
/// <summary>
/// Implements <see cref="BombsAwayGame.IUserInterface"/> by writing to and reading from <see cref="Console"/>.
/// </summary>
internal class ConsoleUserInterface : BombsAwayGame.IUserInterface
{
/// <summary>
/// Write message to console.
/// </summary>
/// <param name="message">Message to display.</param>
public void Output(string message)
{
Console.WriteLine(message);
}
/// <summary>
/// Write choices with affixed indexes, allowing the user to choose by index.
/// </summary>
/// <param name="message">Message to display.</param>
/// <param name="choices">Choices to display.</param>
/// <returns>Choice that user picked.</returns>
public int Choose(string message, IList<string> choices)
{
IEnumerable<string> choicesWithIndexes = choices.Select((choice, index) => $"{choice}({index + 1})");
string choiceText = string.Join(", ", choicesWithIndexes);
Output($"{message} -- {choiceText}");
ISet<ConsoleKey> allowedKeys = ConsoleKeysFromList(choices);
ConsoleKey? choice;
do
{
choice = ReadChoice(allowedKeys);
if (choice is null)
{
Output("TRY AGAIN...");
}
}
while (choice is null);
return ListIndexFromConsoleKey(choice.Value);
}
/// <summary>
/// Convert the given list to its <see cref="ConsoleKey"/> equivalents. This generates keys that map
/// the first element to <see cref="ConsoleKey.D1"/>, the second element to <see cref="ConsoleKey.D2"/>,
/// and so on, up to the last element of the list.
/// </summary>
/// <param name="list">List whose elements will be converted to <see cref="ConsoleKey"/> equivalents.</param>
/// <returns><see cref="ConsoleKey"/> equivalents from <paramref name="list"/>.</returns>
private ISet<ConsoleKey> ConsoleKeysFromList(IList<string> list)
{
IEnumerable<int> indexes = Enumerable.Range((int)ConsoleKey.D1, list.Count);
return new HashSet<ConsoleKey>(indexes.Cast<ConsoleKey>());
}
/// <summary>
/// Convert the given console key to its list index equivalent. This assumes the key was generated from
/// <see cref="ConsoleKeysFromList(IList{string})"/>
/// </summary>
/// <param name="key">Key to convert to its list index equivalent.</param>
/// <returns>List index equivalent of key.</returns>
private int ListIndexFromConsoleKey(ConsoleKey key)
{
return key - ConsoleKey.D1;
}
/// <summary>
/// Read a key from the console and return it if it is in the given allowed keys.
/// </summary>
/// <param name="allowedKeys">Allowed keys.</param>
/// <returns>Key read from <see cref="Console"/>, if it is in <paramref name="allowedKeys"/>; null otherwise./></returns>
private ConsoleKey? ReadChoice(ISet<ConsoleKey> allowedKeys)
{
ConsoleKeyInfo keyInfo = ReadKey();
return allowedKeys.Contains(keyInfo.Key) ? keyInfo.Key : null;
}
/// <summary>
/// Read key from <see cref="Console"/>.
/// </summary>
/// <returns>Key read from <see cref="Console"/>.</returns>
private ConsoleKeyInfo ReadKey()
{
ConsoleKeyInfo result = Console.ReadKey(intercept: false);
// Write a blank line to the console so the displayed key is on its own line.
Console.WriteLine();
return result;
}
/// <summary>
/// Allow user to choose 'Y' or 'N' from <see cref="Console"/>.
/// </summary>
/// <param name="message">Message to display.</param>
/// <returns>True if user chose 'Y', false if user chose 'N'.</returns>
public bool ChooseYesOrNo(string message)
{
Output(message);
ConsoleKey? choice;
do
{
choice = ReadChoice(new HashSet<ConsoleKey>(new[] { ConsoleKey.Y, ConsoleKey.N }));
if (choice is null)
{
Output("ENTER Y OR N");
}
}
while (choice is null);
return choice.Value == ConsoleKey.Y;
}
/// <summary>
/// Get integer by reading a line from <see cref="Console"/>.
/// </summary>
/// <returns>Integer read from <see cref="Console"/>.</returns>
public int InputInteger()
{
bool resultIsValid;
int result;
do
{
string? integerText = Console.ReadLine();
resultIsValid = int.TryParse(integerText, out result);
if (!resultIsValid)
{
Output("PLEASE ENTER A NUMBER");
}
}
while (!resultIsValid);
return result;
}
}

View File

@@ -0,0 +1,26 @@
using BombsAwayConsole;
using BombsAwayGame;
/// Create and play <see cref="Game"/>s using a <see cref="ConsoleUserInterface"/>.
PlayGameWhileUserWantsTo(new ConsoleUserInterface());
void PlayGameWhileUserWantsTo(ConsoleUserInterface ui)
{
do
{
new Game(ui).Play();
}
while (UserWantsToPlayAgain(ui));
}
bool UserWantsToPlayAgain(IUserInterface ui)
{
bool result = ui.ChooseYesOrNo("ANOTHER MISSION (Y OR N)?");
if (!result)
{
Console.WriteLine("CHICKEN !!!");
}
return result;
}

View File

@@ -0,0 +1,22 @@
namespace BombsAwayGame;
/// <summary>
/// Allies protagonist. Can fly missions in a Liberator, B-29, B-17, or Lancaster.
/// </summary>
internal class AlliesSide : MissionSide
{
public AlliesSide(IUserInterface ui)
: base(ui)
{
}
protected override string ChooseMissionMessage => "AIRCRAFT";
protected override IList<Mission> AllMissions => new Mission[]
{
new("LIBERATOR", "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI."),
new("B-29", "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA."),
new("B-17", "YOU'RE CHASING THE BISMARK IN THE NORTH SEA."),
new("LANCASTER", "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.")
};
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace BombsAwayGame;
/// <summary>
/// Represents enemy artillery.
/// </summary>
/// <param name="Name">Name of artillery type.</param>
/// <param name="Accuracy">Accuracy of artillery. This is the `T` variable in the original BASIC.</param>
internal record class EnemyArtillery(string Name, int Accuracy);

View File

@@ -0,0 +1,58 @@
namespace BombsAwayGame;
/// <summary>
/// Plays the Bombs Away game using a supplied <see cref="IUserInterface"/>.
/// </summary>
public class Game
{
private readonly IUserInterface _ui;
/// <summary>
/// Create game instance using the given UI.
/// </summary>
/// <param name="ui">UI to use for game.</param>
public Game(IUserInterface ui)
{
_ui = ui;
}
/// <summary>
/// Play game. Choose a side and play the side's logic.
/// </summary>
public void Play()
{
_ui.Output("YOU ARE A PILOT IN A WORLD WAR II BOMBER.");
Side side = ChooseSide();
side.Play();
}
/// <summary>
/// Represents a <see cref="Side"/>.
/// </summary>
/// <param name="Name">Name of side.</param>
/// <param name="CreateSide">Create instance of side that this descriptor represents.</param>
private record class SideDescriptor(string Name, Func<Side> CreateSide);
/// <summary>
/// Choose side and return a new instance of that side.
/// </summary>
/// <returns>New instance of side that was chosen.</returns>
private Side ChooseSide()
{
SideDescriptor[] sides = AllSideDescriptors;
string[] sideNames = sides.Select(a => a.Name).ToArray();
int index = _ui.Choose("WHAT SIDE", sideNames);
return sides[index].CreateSide();
}
/// <summary>
/// All side descriptors.
/// </summary>
private SideDescriptor[] AllSideDescriptors => new SideDescriptor[]
{
new("ITALY", () => new ItalySide(_ui)),
new("ALLIES", () => new AlliesSide(_ui)),
new("JAPAN", () => new JapanSide(_ui)),
new("GERMANY", () => new GermanySide(_ui)),
};
}

View File

@@ -0,0 +1,21 @@
namespace BombsAwayGame;
/// <summary>
/// Germany protagonist. Can fly missions to Russia, England, and France.
/// </summary>
internal class GermanySide : MissionSide
{
public GermanySide(IUserInterface ui)
: base(ui)
{
}
protected override string ChooseMissionMessage => "A NAZI, EH? OH WELL. ARE YOU GOING FOR";
protected override IList<Mission> AllMissions => new Mission[]
{
new("RUSSIA", "YOU'RE NEARING STALINGRAD."),
new("ENGLAND", "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR."),
new("FRANCE", "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.")
};
}

View File

@@ -0,0 +1,38 @@
namespace BombsAwayGame;
/// <summary>
/// Represents an interface for supplying data to the game.
/// </summary>
/// <remarks>
/// Abstracting the UI allows us to concentrate its concerns in one part of our code and to change UI behavior
/// without creating any risk of changing the game logic. It also allows us to supply an automated UI for tests.
/// </remarks>
public interface IUserInterface
{
/// <summary>
/// Display the given message.
/// </summary>
/// <param name="message">Message to display.</param>
void Output(string message);
/// <summary>
/// Choose an item from the given choices.
/// </summary>
/// <param name="message">Message to display.</param>
/// <param name="choices">Choices to choose from.</param>
/// <returns>Index of choice in <paramref name="choices"/> that user chose.</returns>
int Choose(string message, IList<string> choices);
/// <summary>
/// Allow user to choose Yes or No.
/// </summary>
/// <param name="message">Message to display.</param>
/// <returns>True if user chose Yes, false if user chose No.</returns>
bool ChooseYesOrNo(string message);
/// <summary>
/// Get integer from user.
/// </summary>
/// <returns>Integer supplied by user.</returns>
int InputInteger();
}

View File

@@ -0,0 +1,21 @@
namespace BombsAwayGame;
/// <summary>
/// Italy protagonist. Can fly missions to Albania, Greece, and North Africa.
/// </summary>
internal class ItalySide : MissionSide
{
public ItalySide(IUserInterface ui)
: base(ui)
{
}
protected override string ChooseMissionMessage => "YOUR TARGET";
protected override IList<Mission> AllMissions => new Mission[]
{
new("ALBANIA", "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE."),
new("GREECE", "BE CAREFUL!!!"),
new("NORTH AFRICA", "YOU'RE GOING FOR THE OIL, EH?")
};
}

View File

@@ -0,0 +1,38 @@
namespace BombsAwayGame;
/// <summary>
/// Japan protagonist. Flies a kamikaze mission, which has a different logic from <see cref="MissionSide"/>s.
/// </summary>
internal class JapanSide : Side
{
public JapanSide(IUserInterface ui)
: base(ui)
{
}
/// <summary>
/// Perform a kamikaze mission. If first kamikaze mission, it will succeed 65% of the time. If it's not
/// first kamikaze mission, perform an enemy counterattack.
/// </summary>
public override void Play()
{
UI.Output("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.");
bool isFirstMission = UI.ChooseYesOrNo("YOUR FIRST KAMIKAZE MISSION(Y OR N)?");
if (!isFirstMission)
{
// LINE 207 of original BASIC: hitRatePercent is initialized to 0,
// but R, the type of artillery, is not initialized at all. Setting
// R = 1, which is to say EnemyArtillery = Guns, gives the same result.
EnemyCounterattack(Guns, hitRatePercent: 0);
}
else if (RandomFrac() > 0.65)
{
MissionSucceeded();
}
else
{
MissionFailed();
}
}
}

View File

@@ -0,0 +1,8 @@
namespace BombsAwayGame;
/// <summary>
/// Represents a mission that can be flown by a <see cref="MissionSide"/>.
/// </summary>
/// <param name="Name">Name of mission.</param>
/// <param name="Description">Description of mission.</param>
internal record class Mission(string Name, string Description);

View File

@@ -0,0 +1,208 @@
namespace BombsAwayGame;
/// <summary>
/// Represents a protagonist that chooses a standard (non-kamikaze) mission.
/// </summary>
internal abstract class MissionSide : Side
{
/// <summary>
/// Create instance using the given UI.
/// </summary>
/// <param name="ui">UI to use.</param>
public MissionSide(IUserInterface ui)
: base(ui)
{
}
/// <summary>
/// Reasonable upper bound for missions flown previously.
/// </summary>
private const int MaxMissionCount = 160;
/// <summary>
/// Choose a mission and attempt it. If attempt fails, perform an enemy counterattack.
/// </summary>
public override void Play()
{
Mission mission = ChooseMission();
UI.Output(mission.Description);
int missionCount = MissionCountFromUI();
CommentOnMissionCount(missionCount);
AttemptMission(missionCount);
}
/// <summary>
/// Choose a mission.
/// </summary>
/// <returns>Mission chosen.</returns>
private Mission ChooseMission()
{
IList<Mission> missions = AllMissions;
string[] missionNames = missions.Select(a => a.Name).ToArray();
int index = UI.Choose(ChooseMissionMessage, missionNames);
return missions[index];
}
/// <summary>
/// Message to display when choosing a mission.
/// </summary>
protected abstract string ChooseMissionMessage { get; }
/// <summary>
/// All aviailable missions to choose from.
/// </summary>
protected abstract IList<Mission> AllMissions { get; }
/// <summary>
/// Get mission count from UI. If mission count exceeds a reasonable maximum, ask UI again.
/// </summary>
/// <returns>Mission count from UI.</returns>
private int MissionCountFromUI()
{
const string HowManyMissions = "HOW MANY MISSIONS HAVE YOU FLOWN?";
string inputMessage = HowManyMissions;
bool resultIsValid;
int result;
do
{
UI.Output(inputMessage);
result = UI.InputInteger();
if (result < 0)
{
UI.Output($"NUMBER OF MISSIONS CAN'T BE NEGATIVE.");
resultIsValid = false;
}
else if (result > MaxMissionCount)
{
resultIsValid = false;
UI.Output($"MISSIONS, NOT MILES...{MaxMissionCount} MISSIONS IS HIGH EVEN FOR OLD-TIMERS.");
inputMessage = "NOW THEN, " + HowManyMissions;
}
else
{
resultIsValid = true;
}
}
while (!resultIsValid);
return result;
}
/// <summary>
/// Display a message about the given mission count, if it is unusually high or low.
/// </summary>
/// <param name="missionCount">Mission count to comment on.</param>
private void CommentOnMissionCount(int missionCount)
{
if (missionCount >= 100)
{
UI.Output("THAT'S PUSHING THE ODDS!");
}
else if (missionCount < 25)
{
UI.Output("FRESH OUT OF TRAINING, EH?");
}
}
/// <summary>
/// Attempt mission.
/// </summary>
/// <param name="missionCount">Number of missions previously flown. Higher mission counts will yield a higher probability of success.</param>
private void AttemptMission(int missionCount)
{
if (missionCount < RandomInteger(0, MaxMissionCount))
{
MissedTarget();
}
else
{
MissionSucceeded();
}
}
/// <summary>
/// Display message indicating that target was missed. Choose enemy artillery and perform a counterattack.
/// </summary>
private void MissedTarget()
{
UI.Output("MISSED TARGET BY " + (2 + RandomInteger(0, 30)) + " MILES!");
UI.Output("NOW YOU'RE REALLY IN FOR IT !!");
// Choose enemy and counterattack.
EnemyArtillery enemyArtillery = ChooseEnemyArtillery();
if (enemyArtillery == Missiles)
{
EnemyCounterattack(enemyArtillery, hitRatePercent: 0);
}
else
{
int hitRatePercent = EnemyHitRatePercentFromUI();
if (hitRatePercent < MinEnemyHitRatePercent)
{
UI.Output("YOU LIE, BUT YOU'LL PAY...");
MissionFailed();
}
else
{
EnemyCounterattack(enemyArtillery, hitRatePercent);
}
}
}
/// <summary>
/// Choose enemy artillery from UI.
/// </summary>
/// <returns>Artillery chosen.</returns>
private EnemyArtillery ChooseEnemyArtillery()
{
EnemyArtillery[] artilleries = new EnemyArtillery[] { Guns, Missiles, Both };
string[] artilleryNames = artilleries.Select(a => a.Name).ToArray();
int index = UI.Choose("DOES THE ENEMY HAVE", artilleryNames);
return artilleries[index];
}
/// <summary>
/// Minimum allowed hit rate percent.
/// </summary>
private const int MinEnemyHitRatePercent = 10;
/// <summary>
/// Maximum allowed hit rate percent.
/// </summary>
private const int MaxEnemyHitRatePercent = 50;
/// <summary>
/// Get the enemy hit rate percent from UI. Value must be between zero and <see cref="MaxEnemyHitRatePercent"/>.
/// If value is less than <see cref="MinEnemyHitRatePercent"/>, mission fails automatically because the user is
/// assumed to be untruthful.
/// </summary>
/// <returns>Enemy hit rate percent from UI.</returns>
private int EnemyHitRatePercentFromUI()
{
UI.Output($"WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS ({MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent})");
bool resultIsValid;
int result;
do
{
result = UI.InputInteger();
// Let them enter a number below the stated minimum, as they will be caught and punished.
if (0 <= result && result <= MaxEnemyHitRatePercent)
{
resultIsValid = true;
}
else
{
resultIsValid = false;
UI.Output($"NUMBER MUST BE FROM {MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent}");
}
}
while (!resultIsValid);
return result;
}
}

View File

@@ -0,0 +1,97 @@
namespace BombsAwayGame;
/// <summary>
/// Represents a protagonist in the game.
/// </summary>
internal abstract class Side
{
/// <summary>
/// Create instance using the given UI.
/// </summary>
/// <param name="ui">UI to use.</param>
public Side(IUserInterface ui)
{
UI = ui;
}
/// <summary>
/// Play this side.
/// </summary>
public abstract void Play();
/// <summary>
/// User interface supplied to ctor.
/// </summary>
protected IUserInterface UI { get; }
/// <summary>
/// Random-number generator for this play-through.
/// </summary>
private readonly Random _random = new();
/// <summary>
/// Gets a random floating-point number greater than or equal to zero, and less than one.
/// </summary>
/// <returns>Random floating-point number greater than or equal to zero, and less than one.</returns>
protected double RandomFrac() => _random.NextDouble();
/// <summary>
/// Gets a random integer in a range.
/// </summary>
/// <param name="minValue">The inclusive lower bound of the number returned.</param>
/// <param name="maxValue">The exclusive upper bound of the number returned.</param>
/// <returns>Random integer in a range.</returns>
protected int RandomInteger(int minValue, int maxValue) => _random.Next(minValue: minValue, maxValue: maxValue);
/// <summary>
/// Display messages indicating the mission succeeded.
/// </summary>
protected void MissionSucceeded()
{
UI.Output("DIRECT HIT!!!! " + RandomInteger(0, 100) + " KILLED.");
UI.Output("MISSION SUCCESSFUL.");
}
/// <summary>
/// Gets the Guns type of enemy artillery.
/// </summary>
protected EnemyArtillery Guns { get; } = new("GUNS", 0);
/// <summary>
/// Gets the Missiles type of enemy artillery.
/// </summary>
protected EnemyArtillery Missiles { get; } = new("MISSILES", 35);
/// <summary>
/// Gets the Both Guns and Missiles type of enemy artillery.
/// </summary>
protected EnemyArtillery Both { get; } = new("BOTH", 35);
/// <summary>
/// Perform enemy counterattack using the given artillery and hit rate percent.
/// </summary>
/// <param name="artillery">Enemy artillery to use.</param>
/// <param name="hitRatePercent">Hit rate percent for enemy.</param>
protected void EnemyCounterattack(EnemyArtillery artillery, int hitRatePercent)
{
if (hitRatePercent + artillery.Accuracy > RandomInteger(0, 100))
{
MissionFailed();
}
else
{
UI.Output("YOU MADE IT THROUGH TREMENDOUS FLAK!!");
}
}
/// <summary>
/// Display messages indicating the mission failed.
/// </summary>
protected void MissionFailed()
{
UI.Output("* * * * BOOM * * * *");
UI.Output("YOU HAVE BEEN SHOT DOWN.....");
UI.Output("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR");
UI.Output("LAST TRIBUTE...");
}
}

View File

@@ -1,7 +1,16 @@
### Bounce
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=25
This program plots a bouncing ball. Most computer plots run along the paper in the terminal (top to bottom); however, this plot is drawn horizontally on the paper (left to right).
You may specify the initial velocity of the ball and the coefficient of elasticity of the ball (a superball is about 0.85 — other balls are much less). You also specify the time increment to be used in “strobing” the flight of the ball. In other words, it is as though the ball is thrown up in a darkened room and you flash a light at fixed time intervals and photograph the progress of the ball.
The program was originally written by Val Skalabrin while he was at DEC.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=25)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=40)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,18 @@
### Bowling
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=26
This is a simulated bowling game for up to four players. You play 10 frames. To roll the ball, you simply type “ROLL.” After each roll, the computer will show you a diagram of the remaining pins (“0” means the pin is down, “+” means it is still standing), and it will give you a roll analysis:
- GUTTER
- STRIKE
- SPARE
- ERROR (on second ball if pins still standing)
Bowling was written by Paul Peraino while a student at Woodrow Wilson High School, San Francisco, California.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=26)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=41)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Boxing
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=28
This program simulates a three-round Olympic boxing match. The computer coaches one of the boxers and determines his punches and defences, while you do the same for your boxer. At the start of the match, you may specify your mans best punch and his vulnerability.
There are approximately seven major punches per round, although this may be varied. The best out if three rounds wins.
Jesse Lynch of St. Paul, Minnesota created this program.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=28)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=43)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,18 @@
### Bug
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=30
The object of this game is to finish your drawing of a bug before the computer finishes.
You and the computer roll a die alternately with each number standing for a part of the bug. You must add the parts in the right order; in other words, you cannot have a neck until you have a body, you cannot have a head until you have a neck, and so on. After each new part has been added, you have the option of seeing pictures of the two bugs.
If you elect to see all the pictures, this program has the ability of consuming well over six feet of terminal paper per run. We can only suggest recycling the paper by using the other side.
Brian Leibowitz wrote this program while in the 7th grade at Harrison Jr-Se High School in Harrison, New York.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=30)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=45)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,25 @@
### Bullfight
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=32
In this simulated bullfight, you are the matador — i.e., the one with the principle role and the one who must kill the bull or be killed (or run from the ring).
On each pass of the bull, you may try:
- 0: Veronica (dangerous inside move of the cape)
- 1: Less dangerous outside move of the cape
- 2: Ordinary swirl of the cape
Or you may try to kill the bull:
- 4: Over the horns
- 5: In the chest
The crowd will determine what award you deserve, posthumously if necessary. The braver you are, the better the reward you receive. Its nice to stay alive too. The better the job the picadores and toreadores do, the better your chances.
David Sweet of Dartmouth wrote the original version of this program. It was then modified by students at Lexington High School and finally by Steve North of Creative Computing.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=32)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=47)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,36 @@
### Bullseye
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=34
In this game, up to 20 players throw darts at a target with 10-, 20-, 30-, and 40-point zones. The objective is to get 200 points.
You have a choice of three methods of throwing:
| Throw | Description | Probable Score |
|-------|--------------------|---------------------------|
| 1 | Fast overarm | Bullseye or complete miss |
| 2 | Controlled overarm | 10, 20, or 30 points |
| 3 | Underarm | Anything |
You will find after playing a while that different players will swear by different strategies. However, considering the expected score per throw by always using throw 3:
| Score (S) | Probability (P) | S x P |
|-----------|-----------------|-------|
| 40 | 1.00-.95 = .05 | 2 |
| 30 | .95-.75 = .20 | 6 |
| 30 | .75-.45 = .30 | 6 |
| 10 | .45-.05 = .40 | 4 |
| 0 | .05-.00 = .05 | 0 |
Expected score per throw = 18
Calculate the expected score for the other throws and you may be surprised!
The program was written by David Ahl of Creative Computing.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=34)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=49)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

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}") = "Bullseye", "Bullseye.csproj", "{04C164DB-594F-41C4-BC0E-0A203A5536C7}"
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
{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,237 @@
namespace Bullseye
{
/// <summary>
/// Class encompassing the game
/// </summary>
public class BullseyeGame
{
private readonly List<Player> _players;
// define a constant for the winning score so that it is
// easy to change again in the future
private const int WinningScore = 200;
public BullseyeGame()
{
// create the initial list of players; list is empty, but
// the setup of the game will add items to this list
_players = new List<Player>();
}
public void Run()
{
PrintIntroduction();
SetupGame();
PlayGame();
PrintResults();
}
private void SetupGame()
{
// First, allow the user to enter how many players are going
// to play. This could be weird if the user enters negative
// numbers, words, or too many players, so there are some
// extra checks on the input to make sure the user didn't do
// anything too crazy. Loop until the user enters valid input.
bool validPlayerCount;
int playerCount;
do
{
Console.WriteLine();
Console.Write("HOW MANY PLAYERS? ");
string? input = Console.ReadLine();
// assume the user has entered something incorrect - the
// next steps will validate the input
validPlayerCount = false;
if (Int32.TryParse(input, out playerCount))
{
if (playerCount > 0 && playerCount <= 20)
{
validPlayerCount = true;
}
else
{
Console.WriteLine("YOU MUST ENTER A NUMBER BETWEEN 1 AND 20!");
}
}
else
{
Console.WriteLine("YOU MUST ENTER A NUMBER");
}
}
while (!validPlayerCount);
// Next, allow the user to enter names for the players; as each
// name is entered, create a Player object to track the name
// and their score, and save the object to the list in this class
// so the rest of the game has access to the set of players
for (int i = 0; i < playerCount; i++)
{
string? playerName = String.Empty;
do
{
Console.Write($"NAME OF PLAYER #{i+1}? ");
playerName = Console.ReadLine();
// names can be any sort of text, so allow whatever the user
// enters as long as it isn't a blank space
}
while (String.IsNullOrWhiteSpace(playerName));
_players.Add(new Player(playerName));
}
}
private void PlayGame()
{
Random random = new Random(DateTime.Now.Millisecond);
int round = 0;
bool isOver = false;
do
{
// starting a new round, increment the counter
round++;
Console.WriteLine($"ROUND {round}");
Console.WriteLine("--------------");
foreach (Player player in _players)
{
// ask the user how they want to throw
Console.Write($"{player.Name.ToUpper()}'S THROW: ");
string? input = Console.ReadLine();
// based on the input, figure out the probabilities
int[] probabilities;
switch (input)
{
case "1":
{
probabilities = new int[] { 65, 55, 50, 50 };
break;
}
case "2":
{
probabilities = new int[] { 99, 77, 43, 1 };
break;
}
case "3":
{
probabilities = new int[] { 95, 75, 45, 5 };
break;
}
default:
{
// in case the user types something bad, pretend it's
// as if they tripped over themselves while throwing
// the dart - they'll either hit a bullseye or completely
// miss
probabilities = new int[] { 95, 95, 95, 95 };
Console.Write("TRIP! ");
break;
}
}
// Next() returns a number in the range: min <= num < max, so specify 101
// as the maximum so that 100 is a number that could be returned
int chance = random.Next(0, 101);
if (chance > probabilities[0])
{
player.Score += 40;
Console.WriteLine("BULLSEYE!! 40 POINTS!");
}
else if (chance > probabilities[1])
{
player.Score += 30;
Console.WriteLine("30-POINT ZONE!");
}
else if (chance > probabilities[2])
{
player.Score += 20;
Console.WriteLine("20-POINT ZONE");
}
else if (chance > probabilities[3])
{
player.Score += 10;
Console.WriteLine("WHEW! 10 POINTS.");
}
else
{
// missed it
Console.WriteLine("MISSED THE TARGET! TOO BAD.");
}
// check to see if the player has won - if they have, then
// break out of the loops
if (player.Score > WinningScore)
{
Console.WriteLine();
Console.WriteLine("WE HAVE A WINNER!!");
Console.WriteLine($"{player.Name.ToUpper()} SCORED {player.Score} POINTS.");
Console.WriteLine();
isOver = true; // out of the do/while round loop
break; // out of the foreach (player) loop
}
Console.WriteLine();
}
}
while (!isOver);
}
private void PrintResults()
{
// For bragging rights, print out all the scores, but sort them
// by who had the highest score
var sorted = _players.OrderByDescending(p => p.Score);
// padding is used to get things to line up nicely - the results
// should look something like:
// PLAYER SCORE
// Bravo 210
// Charlie 15
// Alpha 1
Console.WriteLine("PLAYER SCORE");
foreach (var player in sorted)
{
Console.WriteLine($"{player.Name.PadRight(12)} {player.Score.ToString().PadLeft(5)}");
}
Console.WriteLine();
Console.WriteLine("THANKS FOR THE GAME.");
}
private void PrintIntroduction()
{
Console.WriteLine(Title);
Console.WriteLine();
Console.WriteLine(Introduction);
Console.WriteLine();
Console.WriteLine(Operations);
}
private const string Title = @"
BULLSEYE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY";
private const string Introduction = @"
IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET
WITH 10, 20, 30, AND 40 POINT ZONES. THE OBJECTIVE IS
TO GET 200 POINTS.";
private const string Operations = @"
THROW DESCRIPTION PROBABLE SCORE
1 FAST OVERARM BULLSEYE OR COMPLETE MISS
2 CONTROLLED OVERARM 10, 20, OR 30 POINTS
3 UNDERARM ANYTHING";
}
}

View File

@@ -0,0 +1,28 @@
namespace Bullseye
{
/// <summary>
/// Object to track the name and score of a player
/// </summary>
public class Player
{
/// <summary>
/// Creates a play with the given name
/// </summary>
/// <param name="name">Name of the player</param>
public Player(string name)
{
Name = name;
Score = 0;
}
/// <summary>
/// Name of the player
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Current score of the player
/// </summary>
public int Score { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System;
namespace Bullseye
{
public static class Program
{
// Entry point to the application; create an instance of the
// game class and call Run()
public static void Main(string[] args)
{
new BullseyeGame().Run();
}
}
}

View File

@@ -1,7 +1,8 @@
### Bunny
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=35
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=35)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=50)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,14 @@
### Buzzword
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=36
This program is an invaluable aid for preparing speeches and briefings about educational technology. This buzzword generator provides sets of three highly-acceptable words to work into your material. Your audience will never know that the phrases dont really mean much of anything because they sound so great! Full instructions for running are given in the program.
This version of Buzzword was written by David Ahl.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=36)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=51)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.810.15
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buzzword", "Buzzword.csproj", "{E342DEB2-F009-47FD-85F6-E84965EAAB56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {52F15A7F-671A-470D-91CE-F3B629B989B7}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,115 @@
using System;
namespace Buzzword
{
class Program
{
/// <summary>
/// Displays header.
/// </summary>
static void Header()
{
Console.WriteLine("Buzzword generator".PadLeft(26));
Console.WriteLine("Creating Computing Morristown, New Jersey".PadLeft(15));
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
}
// Information for the user about possible key input.
static string keys = "type a 'Y' for another phrase or 'N' to quit";
/// <summary>
/// Displays instructions.
/// </summary>
static void Instructions()
{
Console.WriteLine("This program prints highly acceptable phrases in\n"
+ "'educator-speak' that you can work into reports\n"
+ "and speeches. Whenever a question mark is printed,\n"
+ $"{keys}.");
Console.WriteLine();
Console.WriteLine();
Console.Write("Here's the first phrase:");
}
static string[] Words = new[]
{ "ability", "basal", "behavioral", "child-centered",
"differentiated", "discovery", "flexible", "heterogenous",
"homogeneous", "manipulative", "modular", "tavistock",
"individualized", "learning", "evaluative", "objective",
"cognitive", "enrichment", "scheduling", "humanistic",
"integrated", "non-graded", "training", "vertical age",
"motivational", "creative", "grouping", "modification",
"accountability", "process", "core curriculum", "algorithm",
"performance", "reinforcement", "open classroom", "resource",
"structure", "facility", "environment" };
/// <summary>
/// Capitalizes first letter of given string.
/// </summary>
/// <param name="input"></param>
/// <returns>string</returns>
static string Capitalize(string input)
{
if (string.IsNullOrWhiteSpace(input))
return string.Empty;
return char.ToUpper(input[0]) + input[1..];
}
// Seed has been calculated to get the same effect as in original,
// at least in first phrase
static readonly Random rnd = new Random(1486);
/// <summary>
/// Generates random phrase from words available in Words array.
/// </summary>
/// <returns>String representing random phrase where first letter is capitalized.</returns>
static string GeneratePhrase()
{
// Indexing from 0, so had to decrease generated numbers
return $"{Capitalize(Words[rnd.Next(13)])} "
+ $"{Words[rnd.Next(13, 26)]} "
+ $"{Words[rnd.Next(26, 39)]}";
}
/// <summary>
/// Handles user input. On wrong input it displays information about
/// valid keys in infinite loop.
/// </summary>
/// <returns>True if user pressed 'Y', false if 'N'.</returns>
static bool Decision()
{
while (true)
{
Console.Write("?");
var answer = Console.ReadKey();
if (answer.Key == ConsoleKey.Y)
return true;
else if (answer.Key == ConsoleKey.N)
return false;
else
Console.WriteLine($"\n{keys}");
}
}
static void Main(string[] args)
{
Header();
Instructions();
while (true)
{
Console.WriteLine();
Console.WriteLine(GeneratePhrase());
Console.WriteLine();
if (!Decision())
break;
}
Console.WriteLine("\nCome back when you need help with another report!");
}
}
}

View File

@@ -1,7 +1,23 @@
### Calendar
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=37
This program prints out a calendar for any year. You must specify the starting day of the week of the year:
- 0: Sunday
- -1: Monday
- -2: Tuesday
- -3: Wednesday
- -4: Thursday
- -5: Friday
- -6: Saturday
You can determine this by using the program WEEKDAY. You must also make two changes for leap years. The program listing describes the necessary changes. Running the program produces a nice 12-month calendar.
The program was written by Geoffrey Chase of the Abbey, Portsmouth, Rhode Island.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=37)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=52)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>_21_calendar</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.31613.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "21_calendar", "21_calendar.csproj", "{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1B423C35-492F-4AF8-97FC-BEC69B44EC8D}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,144 @@
using System;
/*
21_Calendar in C# for basic-computer-games
Converted by luminoso-256
*/
namespace _21_calendar
{
class Program
{
//basic has a TAB function. We do not by default, so we make our own!
static string Tab(int numspaces)
{
string space = "";
//loop as many times as there are spaces specified, and add a space each time
while (numspaces > 0)
{
//add the space
space += " ";
//decrement the loop variable so we don't keep going forever!
numspaces--;
}
return space;
}
static void Main(string[] args)
{
// print the "title" of our program
// the usage of Write*Line* means we do not have to specify a newline (\n)
Console.WriteLine(Tab(32) + "CALENDAR");
Console.WriteLine(Tab(15) + "CREATE COMPUTING MORRISTOWN, NEW JERSEY");
//give us some space.
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
//establish some variables needed to print out a calculator
//the length of each month in days. On a leap year, the start of this would be
// 0, 31, 29 to account for Feb. the 0 at the start is for days elapsed to work right in Jan.
int[] monthLengths = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // m in original source
//the starting day of the month. in 1979 this was monday
// 0 = sun, -1 = mon, -2 = tue, -3 = wed, etc.
int day = -1; // called d in original source
//how much time in the year has gone by?
int elapsed = 0; // called s in original source
//loop through printing all the months.
for (int month = 1; month <= 12; month++) //month is called n in original source
{
//pad some space
Console.WriteLine("");
Console.WriteLine("");
//increment days elapsed
elapsed += monthLengths[month - 1];
//build our header for this month of the calendar
string header = "** " + elapsed;
//add padding as needed
while (header.Length < 7)
{
header += " ";
}
for (int i = 1; i <= 18; i++)
{
header += "*";
}
//determine what month it is, add text accordingly
switch (month) {
case 1: header += " JANUARY "; break;
case 2: header += " FEBRUARY"; break;
case 3: header += " MARCH "; break;
case 4: header += " APRIL "; break;
case 5: header += " MAY "; break;
case 6: header += " JUNE "; break;
case 7: header += " JULY "; break;
case 8: header += " AUGUST "; break;
case 9: header += "SEPTEMBER"; break;
case 10: header += " OCTOBER "; break;
case 11: header += " NOVEMBER"; break;
case 12: header += " DECEMBER"; break;
}
//more padding
for (int i = 1; i <= 18; i++)
{
header += "*";
}
header += " ";
// how many days left till the year's over?
header += (365 - elapsed) + " **"; // on leap years 366
Console.WriteLine(header);
//dates
Console.WriteLine(" S M T W T F S");
Console.WriteLine(" ");
string weekOutput = "";
for (int i = 1; i <= 59; i++)
{
weekOutput += "*";
}
//init some vars ahead of time
int g = 0;
int d2 = 0;
//go through the weeks and days
for (int week = 1; week <= 6; week++)
{
Console.WriteLine(weekOutput);
weekOutput = " ";
for (g = 1; g <= 7; g++)
{
//add one to the day
day++;
d2 = day - elapsed;
//check if we're done with this month
if (d2 > monthLengths[month])
{
week = 6;
break;
}
//should we print this day?
if (d2 > 0)
{
weekOutput += d2;
}
//padding
while (weekOutput.Length < 4 + 8 * g)
{
weekOutput += " ";
}
}
if (d2 == monthLengths[month])
{
day += g;
break;
}
}
day -= g;
Console.WriteLine(weekOutput);
}
}
}
}

View File

@@ -1,7 +1,12 @@
### Change
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=39
In this program, the computer pretends it is the cashier at your friendly neighborhood candy store. You tell it the cost of the item(s) you are buying, the amount of your payment, and it will automatically (!) determine your correct change. Arent machines wonderful? Dennis Lunder of Peoples Computer Company wrote this program.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=39)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=54)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.810.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Change", "Change.csproj", "{AE094667-8496-4ECF-8B42-B1648EE26073}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AE094667-8496-4ECF-8B42-B1648EE26073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE094667-8496-4ECF-8B42-B1648EE26073}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE094667-8496-4ECF-8B42-B1648EE26073}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE094667-8496-4ECF-8B42-B1648EE26073}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {65684CBD-CD74-46AF-8E9E-0F69DCF72697}
EndGlobalSection
EndGlobal

129
22_Change/csharp/Program.cs Normal file
View File

@@ -0,0 +1,129 @@
using System;
namespace Change
{
class Program
{
/// <summary>
/// Prints header.
/// </summary>
static void Header()
{
Console.WriteLine("Change".PadLeft(33));
Console.WriteLine("Creative Computing Morristown, New Jersey".PadLeft(15));
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("I, your friendly microcomputer, will determine\n"
+ "the correct change for items costing up to $100.");
Console.WriteLine();
Console.WriteLine();
}
/// <summary>
/// Gets user input for price and payment.
/// </summary>
/// <returns>
/// False if any input can't be parsed to double. Price and payment returned would be 0.
/// True if it was possible to parse inputs into doubles. Price and payment returned
/// would be as provided by the user.
/// </returns>
static (bool status, double price, double payment) GetInput()
{
Console.Write("Cost of item? ");
var priceString = Console.ReadLine();
if (!double.TryParse(priceString, out double price))
{
Console.WriteLine($"{priceString} isn't a number!");
return (false, 0, 0);
}
Console.Write("Amount of payment? ");
var paymentString = Console.ReadLine();
if (!double.TryParse(paymentString, out double payment))
{
Console.WriteLine($"{paymentString} isn't a number!");
return (false, 0, 0);
}
return (true, price, payment);
}
/// <summary>
/// Prints bills and coins for given change.
/// </summary>
/// <param name="change"></param>
static void PrintChange(double change)
{
var tens = (int)(change / 10);
if (tens > 0)
Console.WriteLine($"{tens} ten dollar bill(s)");
var temp = change - (tens * 10);
var fives = (int)(temp / 5);
if (fives > 0)
Console.WriteLine($"{fives} five dollar bill(s)");
temp -= fives * 5;
var ones = (int)temp;
if (ones > 0)
Console.WriteLine($"{ones} one dollar bill(s)");
temp -= ones;
var cents = temp * 100;
var half = (int)(cents / 50);
if (half > 0)
Console.WriteLine($"{half} one half dollar(s)");
temp = cents - (half * 50);
var quarters = (int)(temp / 25);
if (quarters > 0)
Console.WriteLine($"{quarters} quarter(s)");
temp -= quarters * 25;
var dimes = (int)(temp / 10);
if (dimes > 0)
Console.WriteLine($"{dimes} dime(s)");
temp -= dimes * 10;
var nickels = (int)(temp / 5);
if (nickels > 0)
Console.WriteLine($"{nickels} nickel(s)");
temp -= nickels * 5;
var pennies = (int)(temp + 0.5);
if (pennies > 0)
Console.WriteLine($"{pennies} penny(s)");
}
static void Main(string[] args)
{
Header();
while (true)
{
(bool result, double price, double payment) = GetInput();
if (!result)
continue;
var change = payment - price;
if (change == 0)
{
Console.WriteLine("Correct amount, thank you!");
continue;
}
if (change < 0)
{
Console.WriteLine($"Sorry, you have short-changed me ${price - payment:N2}!");
continue;
}
Console.WriteLine($"Your change ${change:N2}");
PrintChange(change);
Console.WriteLine("Thank you, come again!");
Console.WriteLine();
}
}
}
}

68
22_Change/perl/change.pl Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/perl
use v5.24; # for say and use strict
use warnings;
sub get_pennies {
my $query = shift;
print "$query? ";
my $in = <>;
chomp $in;
$in =~ /([\d.]+)/; # the first match of digits and decimal points
return int( $1 * 100 );
}
sub make_change {
my $change = shift;
state %change_options = (
'Penny' => { value => 1, plural => 'Pennies' },
'Nickel' => { value => 5 },
'Dime' => { value => 10 },
'Quarter' => { value => 25 },
'One Half Dollar' => { value => 50 },
'One Dollar Bill' => { value => 100 * 1 },
'Five Dollar Bill' => { value => 100 * 5 },
'10 Dollar Bill' => { value => 100 * 10 },
);
foreach my $unit ( sort { $change_options{$b}->{value} <=> $change_options{$a}->{value} } keys %change_options ) {
my $value = $change_options{$unit}->{value};
next if $value > $change;
my $number = int( $change / $value );
if ( $number > 1 ) {
$unit = exists $change_options{$unit}->{plural} ? $change_options{$unit}->{plural} : "${unit}s";
}
say "$number $unit";
$change -= $number * $value;
}
}
print <<'__END_OF_INTRO';
Change
Creative Computing Morristown, New Jersey
I, Your friendly microcomputer, will determine
the correct change for items costing up to $100.
__END_OF_INTRO
while ( 1 ) {
my $cost = get_pennies( 'Cost of item' );
my $payment = get_pennies( 'Amount of payment');
my $change = $payment - $cost;
my $change_formatted = sprintf( "%.2f", $change / 100 );
if ( $change == 0 ) {
say 'Correct amount, thank you.';
} elsif ( $change < 0 ) {
say 'Sorry, you have short-changed me $', abs($change_formatted);
} else {
say 'Your change, $', $change_formatted;
make_change( $change );
say "Thank you, come again\n\n";
}
}

View File

@@ -1,7 +1,16 @@
### Checkers
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=40
This program plays checkers. The pieces played by the computer are marked with an “X”, yours are marked “O”. A move is made by specifying the coordinates of the piece to be moved (X, Y). Home (0,0) is in the bottom left and X specifies distance to the right of home (i.e., column) and Y specifies distance above home (i.e. row). You then specify where you wish to move to.
THe original version of the program by Alan Segal was not able to recognize (or permit) a double or triple jump. If you tried one, it was likely that your piece would disappear altogether!
Steve North of Creative Computing rectified this problem and Lawrence Neal contributed modifications to allow the program to tell which player has won the game. The computer does not play a particularly good game but we leave it to _you_ to improve that.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=40)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=55)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,14 @@
### Chemist
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=42
The fictitious chemical, kryptocyanic acid, can only be diluted by the ratio of 7 parts water to 3 parts acid. Any other ratio causes an unstable compound which soon explodes. Given an amount of acid, you must determine how much water to add to the dilution. If youre more than 5% off, you lose one of your nine lives. The program continues to play until you lose all nine lives or until it is interrupted.
It was originally written by Wayne Teeter of Ridgecrest, California.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=42)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=57)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -8,15 +8,15 @@ print "THE FICTITIOUS CHECMICAL KRYPTOCYANIC ACID CAN ONLY BE\n";
print "DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID.\n";
print "IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE\n";
print "AND SOON EXPLODES. GIVEN THE AMOUNT OF ACID, YOU MUST\n";
print "DECIDE WHO MUCH WATER TO ADD FOR DILUTION. IF YOU MISS\n";
print "DECIDE HOW MUCH WATER TO ADD FOR DILUTION. IF YOU MISS\n";
print "YOU FACE THE CONSEQUENCES.\n";
my $T=0;
while ($T<9) {
my $A= int(rand(1)*50);
my $A= int(rand(50) + 1);
my $W= 7*$A/3;
print "$A LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER ($W)";
print "? "; chomp(my $R = <STDIN>);
print " $A LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER? ";
chomp(my $R = <STDIN>);
my $D= abs($W-$R);
if ($D>$W/20) {
print "SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB\n";

View File

@@ -1,7 +1,16 @@
### Chief
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=43
In the words of the program author, John Graham, “CHIEF is designed to give people (mostly kids) practice in the four operations (addition, multiplication, subtraction, and division).
It does this while giving people some fun. And then, if the people are wrong, it shows them how they should have done it.
CHIEF was written by John Graham of Upper Brookville, New York.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=43)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=58)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Chomp
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=44
This program is an adaptation of a mathematical game originally described by Martin Gardner in the January 1973 issue of _Scientific American_. Up to a 9x9 grid is set up by you with the upper left square in a poison square. This grid is the cookie. Players alternately chomp away at the cookie from the lower right. To take a chomp, input a row and column number of one of the squares remaining on the cookie. All of the squares below and to the right of that square, including that square, disappear.
Any number of people can play — the computer is only the moderator; it is not a player. Two-person strategies are interesting to work out but strategies when three or more people are playing are the real challenge.
The computer version of the game was written by Peter Sessions of Peoples Computer Company.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=44)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=59)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

173
26_Chomp/perl/chomp.pl Normal file
View File

@@ -0,0 +1,173 @@
#!/usr/bin/perl
my @cookie;
&main;
sub main {
my $answer = 1;
until ($answer == 0) {
$answer=&game_play;
}
}
sub game_play {
# the initial game set up
&print_intro;
my ($players,$rows,$cols) = &get_user_info;
&create_gameboard($rows,$cols);
&print_gameboard($rows,$cols);
my $game_over = 0;
my $player = 0;
# continuous loop until the poison pill is swallowed
until ($game_over == 1) {
if ($player > ($players-1)) { #checks to make sure we're just looping thru valid players
$player = 0;
}
$player++;
my ($user_row,$user_col) = &get_player_row_col($player,$rows,$cols);
if ($cookie[$user_row][$user_col] == -1) {
print "YOU LOSE, PLAYER $player\n\n";
print "AGAIN (1=YES, 0=NO!)\n";
my $answer=<STDIN>;
chomp($answer);
return($answer);
}
&modify_gameboard($rows,$cols,$user_row,$user_col);
&print_gameboard($rows,$cols);
}
}
sub get_player_row_col {
my ($player,$row,$col) = @_;
my @coords;
my $validity="invalid";
# Getting coordinates from user
until ($validity eq "valid") {
print "PLAYER $player COORDINATES OF CHOMP (ROW,COLUMN)\n";
my $input=<STDIN>;
chomp($input);
@coords = split/,/,$input;
#verifying coordinates are valid
if ($coords[0] < 1 || $coords[0] > $row || $coords[1] < 1 || $coords[1] > $col || $cookie[$coords[0]][$coords[1]] == 0) {
print "NO FAIR. YOU'RE TRYING TO CHOMP ON EMPTY SPACE!\n";
}
else {
$validity="valid";
}
}
return($coords[0],$coords[1]);
}
sub get_user_info {
my ($players,$rows,$cols)=0;
until ($players > 0) {
print "HOW MANY PLAYERS\n";
$players=<STDIN>;
chomp($players);
}
until ($rows > 0 && $rows < 10) {
print "HOW MANY ROWS\n";
$rows=<STDIN>;
chomp($rows);
if ($rows > 9) {
print "TOO MANY ROWS (9 IS MAXIMUM). NOW, ";
}
}
until ($cols > 0 && $cols < 10) {
print "HOW MANY COLUMNS\n";
$cols=<STDIN>;
chomp($cols);
if ($cols > 9) {
print "TOO MANY COLUMNS (9 IS MAXIMUM). NOW, ";
}
}
return($players,$rows,$cols);
}
sub print_intro{
print ' ' x 33 . "CHOMP\n";
print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n";
print "THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)\n";
print "DO YOU WANT THE RULES (1=YES, 0=NO!)";
my $answer = <STDIN>;
chomp($answer);
if ($answer == 0) {
return;
}
else {
print "CHOMP IS FOR 1 OR MORE PLAYERS (HUMANS ONLY).\n\n";
print "HERE'S HOW A BOARD LOOKS (THIS ONE IS 5 BY 7):\n";
&create_gameboard(5,7);
&print_gameboard(5,7);
print "THE BOARD IS A BIG COOKIE - R ROWS HIGH AND C COLUMNS\n";
print "WIDE. YOU INPUT R AND C AT THE START. IN THE UPPER LEFT\n";
print "CORNER OF THE COOKIE IS A POISON SQUARE (P). THE ONE WHO\n";
print "CHOMPS THE POISON SQUARE LOSES. TO TAKE A CHOMP, TYPE THE\n";
print "ROW AND COLUMN OF ONE OF THE SQUARES ON THE COOKIE.\n";
print "ALL OF THE SQUARES BELOW AND TO THE RIGHT OF THAT SQUARE\n";
print "(INCLUDING THAT SQUARE, TOO) DISAPPEAR -- CHOMP!!\n";
print "NO FAIR CHOMPING SQUARES THAT HAVE ALREADY BEEN CHOMPED,\n";
print "OR THAT ARE OUTSIDE THE ORIGINAL DIMENSIONS OF THE COOKIE.\n\n";
print "HERE WE GO...\n";
undef @cookie;
}
}
#initial creation of the gameboard
sub create_gameboard {
my $rows = shift;
my $cols = shift;
foreach my $row (1..($rows)) {
foreach my $col (1..($cols)) {
$cookie[$row][$col]=1;
}
}
$cookie[1][1]=-1;
}
#modification of the gameboard based on the input from the player
sub modify_gameboard {
my ($rows,$cols,$user_row,$user_col) = @_;
foreach my $row ($user_row..($rows)) {
foreach my $col ($user_col..($cols)) {
$cookie[$row][$col]=" ";
}
}
}
#prints the gameboard based on the current state of the gameboard
sub print_gameboard {
my ($rows,$cols) = @_;
foreach my $col (1..$cols) {
if ($col == $cols) {
print "$col\n";
}
elsif ($col == 1) {
print "\t $col ";
}
else {
print "$col ";
}
}
foreach my $row (1..($rows)) {
print "\t$row ";
foreach my $col (1..($cols)) {
if ($cookie[$row][$col] == 1) {
print "* ";
}
if ($cookie[$row][$col] == 0) {
print " ";
}
if ($cookie[$row][$col] == -1) {
print "P ";
}
}
print "\n";
}
}

View File

@@ -1,7 +1,18 @@
### Civil War
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=46
This simulation is based on 14 battles in the Civil War. Facts and figures are based on the actual occurrence. If you follow the same strategy used in the actual battle, the results will be the same. Generally, this is a good strategy since the generals in the Civil War were fairly good military strategists. However, you can frequently outperform the Civil War generals, particularly in cases where they did not have good enemy intelligence and consequently followed a poor course of action. Naturally, it helps to know your Civil War history, although the computer gives yuo the rudiments.
After each of the 14 battles, your casualties are compared to the actual casualties of the battle, and you are told whether you win or lose the battle.
You may play Civil War alone in which case the program simulates the Union general. Or two players may play in which case the computer becomes the moderator.
Civil War was written in 1968 by three Students in Lexington High School, Massachusetts: L. Cram, L. Goodie, and D. Hibbard. It was modified into a 2-player game by G. Paul and R. Hess of TIES, St. Paul, Minnesota.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=46)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=61)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Combat
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=50
In this game, you are fighting a small-scale war with the computer. You have 72,000 troops which you first ust distribute among your Army, Navy, and Air Force. You may distribute them in any way you choose as long as you dont use more than 72,000.
You then attack your opponent (the computer) and input which service and the number of men you wish to use. The computer then tells you the outcome of the battle, gives you the current statistics and allows you to determine your next move.
After the second battle, it is decided from the total statistics whether you win or lose or if a treaty is signed.
This program was created by Bob Dores of Milton, Massachusetts.
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=50)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=65)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,19 @@
### Craps
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=52
This game simulates the game of craps played according to standard Nevada craps table rules. That is:
1. A 7 or 11 on the first roll wins
2. A 2, 3, or 12 on the first roll loses
3. Any other number rolled becomes your “point.”
- You continue to roll, if you get your point, you win.
- If you roll a 7, you lose and the dice change hands when this happens.
This version of craps was modified by Steve North of Creative Computing. It is based on an original which appeared one day on a computer at DEC.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=52)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=67)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

79
29_Craps/python/craps.py Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""This game simulates the games of craps played according to standard Nevada craps table rules.
That is:
1. A 7 or 11 on the first roll wins
2. A 2, 3, or 12 on the first roll loses
3. Any other number rolled becomes your "point." You continue to roll; if you get your point you win. If you
roll a 7, you lose and the dice change hands when this happens.
This version of craps was modified by Steve North of Creative Computing. It is based on an original which
appeared one day one a computer at DEC.
"""
from random import randint
def throw_dice():
return randint(1, 6) + randint(1, 6)
print(" " * 33 + "Craps")
print(" " * 15 + "Creative Computing Morristown, New Jersey")
print()
print()
print()
winnings = 0
print("2,3,12 are losers; 4,5,6,8,9,10 are points; 7,11 are natural winners.")
play_again = True
while play_again:
wager = int(input("Input the amount of your wager: "))
print("I will now throw the dice")
roll_1 = throw_dice()
if roll_1 in [7, 11]:
print(f"{roll_1} - natural.... a winner!!!!")
print(f"{roll_1} pays even money, you win {wager} dollars")
winnings += wager
elif roll_1 == 2:
print(f"{roll_1} - snake eyes.... you lose.")
print(f"You lose {wager} dollars")
winnings -= wager
elif roll_1 in [3, 12]:
print(f"{roll_1} - craps.... you lose.")
print(f"You lose {wager} dollars")
winnings -= wager
else:
print(f"{roll_1} is the point. I will roll again")
roll_2 = 0
while roll_2 not in [roll_1, 7]:
roll_2 = throw_dice()
if roll_2 == 7:
print(f"{roll_2} - craps. You lose.")
print(f"You lose $ {wager}")
winnings -= wager
elif roll_2 == roll_1:
print(f"{roll_1} - a winner.........congrats!!!!!!!!")
print(f"{roll_1} at 2 to 1 odds pays you...let me see... {2 * wager} dollars")
winnings += 2 * wager
else:
print(f"{roll_2} - no point. I will roll again")
m = input(" If you want to play again print 5 if not print 2: ")
if winnings < 0:
print(f"You are now under ${-winnings}")
elif winnings > 0:
print(f"You are now ahead ${winnings}")
else:
print("You are now even at 0")
play_again = (m == "5")
if winnings < 0:
print(f"Too bad, you are in the hole. Come again.")
elif winnings > 0:
print(f"Congratulations---you came out a winner. Come again.")
else:
print(f"Congratulations---you came out even, not bad for an amateur")

View File

@@ -1,7 +1,14 @@
### Cube
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=53
CUBE is a game played on the facing sides of a cube with a side dimension of 2. A location is designated by three numbers — e.g., 1, 2, 1. The object is to travel from 1, 1, 1 to 3, 3, 3 by moving one horizontal or vertical (not diagonal) square at a time without striking one of 5 randomly placed landmines. You are staked to $500; prior to each play of the game you may make a wager whether you will reach your destination. You lose if you hit a mine or try to make an illegal move — i.e., change more than one digit from your previous position.
Cube was created by Jerimac Ratliff of Fort Worth, Texas.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=53)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=68)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Depth Charge
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=55
In this program you are captain of the destroyer USS Computer. An enemy submarine has been causing trouble and your mission is to destroy it. You may select the seize of the “cube” of water you wish to search in. The computer then determines how many depth charges you get to destroy the submarine.
Each depth charge is exploded by you specifying a trio of numbers; the first two are the surface coordinates (X,Y), the third is the depth. After each depth charge, your sonar observer will tell you where the explosion was relative to the submarine.
Dana Noftle wrote this program while a student at Acton High School, Acton, Massachusetts.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=55)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=70)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,69 @@
# EditorConfig is awesome: https://EditorConfig.org
# .editorconfig
# Please see doc/developer_notes.md
# If you find anything egregious or missing, please consider submitting a pull request
# to https://github.com/theias/ias_package_shell
# top-most EditorConfig file
root = true
# Sensible defaults for everything
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
# JavaScript
[**.js]
indent_style = space
indent_size = 2
insert_final_newline = true
# Ruby
[**.rb]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Python
[**.py]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
# Perl
[**.pl]
charset = utf-8
insert_final_newline = true
[**.pm]
charset = utf-8
insert_final_newline = true
# PHP
[**.php]
charset = utf-8
indent_size = 4
indent_style = space
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
# Makefiles
[Makefile]
indent_style = tab
[**.gmk]
indent_style = tab
# Configuration Files
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
# Diff files
[*.{diff,patch}]
trim_trailing_whitespace = false

View File

@@ -0,0 +1,211 @@
#!/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()
end
# 420 PRINT "OK. HOPE YOU ENJOYED YOURSELF." : GOTO 600
printf("OK. HOPE YOU ENJOYED YOURSELF.\n")
end
def output_title
printf("--- DEPTH CHARGE ---\n")
printf("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n")
printf("\n")
end
def get_input_y_or_n(message)
while true
print(message)
value = gets.chomp
if (value == 'Y' || value == 'y')
return true
elsif value == 'N' || value == 'n'
return false
end
printf("PLEASE ENTER Y/y OR N/n...\n\n")
end
end
def get_input_positive_integer(message)
while true
print(message)
value = gets.chomp
if (value == 'd')
debug_game()
next
end
the_input = Integer(value) rescue nil
if the_input == nil || the_input < 0
printf("PLEASE ENTER A POSITIVE NUMBER\n\n")
next
end
return the_input
end
end
def get_search_area_dimension
# 20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT
@search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
# 30 N=INT(LOG(G)/LOG(2))+1
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2)
)
end
def print_instructions
# 40 PRINT "YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER"
# 50 PRINT "AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR"
# 60 PRINT "MISSION IS TO DESTROY IT. YOU HAVE";N;"SHOTS."
# 70 PRINT "SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A"
# 80 PRINT "TRIO OF NUMBERS -- THE FIRST TWO ARE THE"
# 90 PRINT "SURFACE COORDINATES; THE THIRD IS THE DEPTH."
# 100 PRINT : PRINT "GOOD LUCK !": PRINT
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.
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
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")
end
def setup_game
get_search_area_dimension()
setup_enemy()
end
def setup_enemy
# 110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1))
@enemy_x = rand(1..@search_area_dimension)
@enemy_y = rand(1..@search_area_dimension)
@enemy_z = rand(1..@search_area_dimension)
end
def game_loop
# 120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z
for @trial in 1..@num_tries do
output_game_status()
@shot_x = get_input_positive_integer("X: ")
@shot_y = get_input_positive_integer("Y: ")
@shot_z = get_input_positive_integer("Z: ")
# 130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300
if (
(@enemy_x - @shot_x).abs \
+ (@enemy_y - @shot_y).abs \
+ (@enemy_z - @shot_z).abs \
== 0
)
you_win()
return
else
# 140 GOSUB 500 : PRINT : NEXT D
missed_shot()
end
end
printf("\n")
you_lose()
end
def output_game_status
printf("YOU HAVE %d SHOTS REMAINING.\n", @num_tries - @trial + 1)
printf("TRIAL \#%d\n", @trial)
end
def you_win
printf("B O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
end
def missed_shot
missed_directions = []
# 530 IF X>A THEN PRINT "EAST";
# 540 IF X<A THEN PRINT "WEST";
if @shot_x > @enemy_x
missed_directions.push('TOO FAR EAST')
elsif @shot_x < @enemy_x
missed_directions.push('TOO FAR WEST')
end
# 510 IF Y>B THEN PRINT "NORTH";
# 520 IF Y<B THEN PRINT "SOUTH";
if @shot_y > @enemy_y
missed_directions.push('TOO FAR NORTH')
elsif @shot_y < @enemy_y
missed_directions.push('TOO FAR SOUTH')
end
# 560 IF Z>C THEN PRINT " TOO LOW."
# 570 IF Z<C THEN PRINT " TOO HIGH."
# 580 IF Z=C THEN PRINT " DEPTH OK."
if @shot_z > @enemy_z
missed_directions.push('TOO DEEP')
elsif @shot_z < @enemy_z
missed_directions.push('TOO SHALLOW')
end
# 500 PRINT "SONAR REPORTS SHOT WAS ";
printf("SONAR REPORTS SHOT WAS: \n")
printf("%s\n", "\t" + missed_directions.join("\n\t"))
# 550 IF Y<>B OR X<>A THEN PRINT " AND";
# 590 RETURN
end
def you_lose
# You took too long!
printf("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n")
printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z)
end
def get_input_another_game
# 400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$
return get_input_y_or_n("ANOTHER GAME (Y OR N): ")
# 410 IF A$="Y" THEN 100
end
end
game = DepthCharge.new
game.run_game()

View File

@@ -1,7 +1,14 @@
### Diamond
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=56
This program fills an 8.5x11 piece of paper with diamonds (plotted on a hard-copy terminal, of course). The program asks for an odd number to be input in the range 5 to 31. The diamonds printed will be this number of characters high and wide. The number of diamonds across the page will vary from 12 for 5-character wide diamonds to 1 for a diamond 31-characters wide. You can change the content of the pattern if you wish.
The program was written by David Ahl of Creative Computing.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=56)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=71)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,21 @@
### Dice
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=57
Not exactly a game, this program simulates rolling a pair of dice a large number of times and prints out the frequency distribution. You simply input the number of rolls. It is interesting to see how many rolls are necessary to approach the theoretical distribution:
| | | |
|---|------|------------|
| 2 | 1/36 | 2.7777...% |
| 3 | 2/36 | 5.5555...% |
| 4 | 3/36 | 8.3333...% |
etc.
Daniel Freidus wrote this program while in the seventh grade at Harrison Jr-Sr High School, Harrison, New York.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=57)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=72)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,17 @@
### Digits
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=58
The player writes down a set of 30 numbers (0, 1, or 2) at random prior to playing the game. The computer program, using pattern recognition techniques, attempts to guess the next number in your list.
The computer asks for 10 numbers at a time. It always guesses first and then examines the next number to see if it guessed correctly. By pure luck (or chance or probability), the computer ought to be right 10 times. It is uncanny how much better it generally does than that!
This program originated at Dartmouth; original author unknown.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=58)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=73)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,18 @@
### Even Wins
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=60
This is a game between you and the computer. To play, an odd number of objects (marbles, chips, matches) are placed in a row. You take turns with the computer picking up between one and four objects each turn. The game ends when there are no objects left, and the winner is the one with an even number of objects picked up.
Two versions of this game are included. While to the player they appear similar, the programming approach is quite different. EVEN WINS, the first version, is deterministic — i.e., the computer plays by fixed, good rules and is impossible to beat if you dont know how to play the game. It always starts with 27 objects, although you may change this.
The second version, GAME OF EVEN WINS, is much more interesting because the computer starts out only knowing the rules of the game. Using simple techniques of artificial intelligence (cybernetics), the computer gradually learns to play this game from its mistakes until it plays a very good game. After 20 games, the computer is a challenge to beat. Variation in the humans style of play seems to make the computer learn more quickly. If you plot the learning curve of this program, it closely resembles classical human learning curves from psychological experiments.
Eric Peters at DEC wrote the GAME OF EVEN WINS. The original author of EVEN WINS is unknown.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=60)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=75)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,206 @@
# evenwins.py
#
# This version of evenwins.bas based on game decscription and does *not*
# follow the source. The computer chooses marbles at random.
#
# For simplicity, global variables are used to store the game state.
# A good exercise would be to replace this with a class.
#
# The code is not short, but hopefully it is easy for beginners to understand
# and modify.
#
# Infinite loops of the style "while True:" are used to simplify some of the
# code. The "continue" keyword is used in a few places to jump back to the top
# of the loop. The "return" keyword is also used to break out of functions.
# This is generally considered poor style, but in this case it simplifies the
# code and makes it easier to read (at least in my opinion). A good exercise
# would be to remove these infinite loops, and uses of continue, to follow a
# more structured style.
#
import random
# global variables
marbles_in_middle = -1
human_marbles = -1
computer_marbles = -1
whose_turn = ''
# Only called during development for serious errors that are due to mistakes
# in the program. Should never be called during a regular game.
def serious_error(msg):
print('serious_error: ' + msg)
exit(1)
def welcome_screen():
print('Welcome to Even Wins!')
print('Based on evenwins.bas from Creative Computing')
print()
print('Even Wins is a two-person game. You start with')
print('27 marbles in the middle of the table.')
print()
print('Players alternate taking marbles from the middle.')
print('A player can take 1 to 4 marbles on their turn, and')
print('turns cannot be skipped. The game ends when there are')
print('no marbles left, and the winner is the one with an even')
print('number of marbles.')
print()
def marbles_str(n):
if n == 1: return '1 marble'
return f'{n} marbles'
def choose_first_player():
global whose_turn
while True:
ans = input('Do you want to play first? (y/n) --> ')
if ans == 'y':
whose_turn = 'human'
return
elif ans == 'n':
whose_turn = 'computer'
return
else:
print()
print('Please enter "y" if you want to play first,')
print('or "n" if you want to play second.')
print()
def next_player():
global whose_turn
if whose_turn == 'human':
whose_turn = 'computer'
elif whose_turn == 'computer':
whose_turn = 'human'
else:
serious_error(f'play_game: unknown player {whose_turn}')
# Converts a string s to an int, if possible.
def to_int(s):
try:
n = int(s)
return True, n
except:
return False, 0
def print_board():
global marbles_in_middle
global human_marbles
global computer_marbles
print()
print(f' marbles in the middle: {marbles_in_middle} ' + marbles_in_middle*'*')
print(f' # marbles you have: {human_marbles}')
print(f'# marbles computer has: {computer_marbles}')
print()
def human_turn():
global marbles_in_middle
global human_marbles
# get number in range 1 to min(4, marbles_in_middle)
max_choice = min(4, marbles_in_middle)
print("It's your turn!")
while True:
s = input(f'Marbles to take? (1 - {max_choice}) --> ')
ok, n = to_int(s)
if not ok:
print()
print(f' Please enter a whole number from 1 to {max_choice}')
print()
continue
if n < 1:
print()
print(' You must take at least 1 marble!')
print()
continue
if n > max_choice:
print()
print(f' You can take at most {marbles_str(max_choice)}')
print()
continue
print()
print(f'Okay, taking {marbles_str(n)} ...')
marbles_in_middle -= n
human_marbles += n
return
def game_over():
global marbles_in_middle
global human_marbles
global computer_marbles
print()
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print('!! All the marbles are taken: Game Over!')
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print()
print_board()
if human_marbles % 2 == 0:
print('You are the winner! Congratulations!')
else:
print('The computer wins: all hail mighty silicon!')
print('')
def computer_turn():
global marbles_in_middle
global computer_marbles
print("It's the computer's turn ...")
max_choice = min(4, marbles_in_middle)
# choose at random
n = random.randint(1, max_choice)
print(f'Computer takes {marbles_str(n)} ...')
marbles_in_middle -= n
computer_marbles += n
def play_game():
global marbles_in_middle
global human_marbles
global computer_marbles
# initialize the game state
marbles_in_middle = 27
human_marbles = 0
computer_marbles = 0
print_board()
while True:
if marbles_in_middle == 0:
game_over()
return
elif whose_turn == 'human':
human_turn()
print_board()
next_player()
elif whose_turn == 'computer':
computer_turn()
print_board()
next_player()
else:
serious_error(f'play_game: unknown player {whose_turn}')
def main():
global whose_turn
welcome_screen()
while True:
choose_first_player()
play_game()
# ask if the user if they want to play again
print()
again = input('Would you like to play again? (y/n) --> ')
if again == 'y':
print()
print("Ok, let's play again ...")
print()
else:
print()
print('Ok, thanks for playing ... goodbye!')
print()
return
if __name__ == '__main__':
main()

View File

@@ -1,7 +1,28 @@
### Flip Flop
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=63
The object of this game is to change a row of ten Xs
```
X X X X X X X X X X
```
to a row of ten 0s
```
0 0 0 0 0 0 0 0 0 0
```
by typing in a number corresponding to the position of an “X” in the line. On some numbers one position will change while on other numbers, two will change. For example, inputting a 3 may reverse the X and 0 in position 3, but it might possibly reverse some of other position too! You ought to be able to change all 10 in 12 or fewer moves. Can you figure out a good winning strategy?
To reset the line to all Xs (same game), type 0 (zero). To start a new game at any point, type 11.
The original author of this game was Micheal Kass of New Hyde Park, New York.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=63)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=78)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -0,0 +1,129 @@
# Flip Flop
#
# The object of this game is to change a row of ten X's
# X X X X X X X X X X
# to a row of ten O's:
# O O O O O O O O O O
# by typing in a number corresponding
# to the position of an "X" in the line. On
# some numbers one position will
# change while on other numbers, two
# will change. For example, inputting a 3
# may reverse the X and O in position 3,
# but it might possibly reverse some
# other position too! You ought to be able
# to change all 10 in 12 or fewer
# moves. Can you figure out a good win-
# ning strategy?
# To reset the line to all X's (same
# game), type 0 (zero). To start a new
# game at any point, type 11.
# The original author of this game was
# Michael Kass of New Hyde Park, New
# York.
import random
import math
from typing import Callable, List, Tuple
flip_dict = {"X": "O", "O": "X"}
def flip_bits(
row: List[str], m: int, n: int, r_function: Callable[[int], float]
) -> Tuple[List[str], int]:
"""
Function that flips the positions at the computed steps
"""
while m == n:
r = r_function(n)
n = r - int(math.floor(r))
n = int(10 * n)
if row[n] == "X":
row[n] = "O"
break
elif row[n] == "O":
row[n] = "X"
return row, n
def print_instructions():
print(" " * 32 + "FLIPFLOP")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print("\n" * 2)
print("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n")
print("X X X X X X X X X X\n")
print("TO THIS:\n")
print("O O O O O O O O O O\n")
print("BY TYPING TH 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).\n")
def main():
q = random.random()
print("HERE IS THE STARTING LINE OF X'S.\n")
# We add an extra 0-th item because this sometimes is set to something
# but we never check what it is for completion of the puzzle
row = [""] + ["X"] * 10
counter_turns = 0
n = -1
legal_move = True
while row[1:] != ["O"] * 10:
if legal_move:
print(" ".join([str(i) for i in range(1, 11)]))
print(" ".join(row[1:]) + "\n")
m = input("INPUT THE NUMBER\n")
try:
m = int(m)
if m > 11 or m < 0:
raise ValueError()
except ValueError:
print("ILLEGAL ENTRY--TRY AGAIN")
legal_move = False
continue
legal_move = True
if m == 11:
# completely reset the puzzle
counter_turns = 0
row = [""] + ["X"] * 10
q = random.random()
continue
elif m == 0:
# reset the board, but not the counter or the random number
row = [""] + ["X"] * 10
elif m == n:
row[n] = flip_dict[row[n]]
r_function = lambda n_t: 0.592 * (1 / math.tan(q / n_t + q)) / math.sin(
n_t * 2 + q
) - math.cos(n_t)
row, n = flip_bits(row, m, n, r_function)
else:
n = m
row[n] = flip_dict[row[n]]
r_function = lambda n_t: (
math.tan(q + n_t / q - n_t)
- math.sin(n_t * 2 + q)
+ 336 * math.sin(8 * n_t)
)
row, n = flip_bits(row, m, n, r_function)
counter_turns += 1
print()
if counter_turns <= 12:
print(f"VERY GOOD. YOU GUESSED IT IN ONLY {counter_turns} GUESSES.")
else:
print(f"TRY HARDER NEXT TIME. IT TOOK YOU {counter_turns} GUESSES.")
return
if __name__ == "__main__":
print_instructions()
another = ""
while another != "NO":
main()
another = input("DO YOU WANT TO TRY ANOTHER PUZZLE\n")

View File

@@ -1,7 +1,18 @@
### Football
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=64
Football is probably the most popular simulated sports game. I have seen some people play to elect to play computerized football in preference to watching a football game on television.
Two versions of football are presented. The first is somewhat “traditional” in that you, the player, are playing against the computer. You have a choice of seven offensive plays. On defense the computer seems to play a zone defence, but you have no choice of plays. The computer program presents the necessary rules as you play, and it is also the referee and determines penalties when an infraction is committed. FTBALL was written by John Kemeny at Dartmouth.
IN the second version of football, the computer referees a game played between two human players. Each player gets a list of twenty plays with a code value for each one. This list should be kept confidential from your opponent. The codes can be changes in data. All twenty plays are offensive; a defensive play is specified by defending against a type of offensive play. A defense is good for other similar types of plays, for example, a defense against a flare pass is very good against a screen pass but much less good against a half-back option.
This game was originally written by Raymond Miseyka of Butler, Pennsylvania.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=64)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=79)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Fur Trader
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=69
You are the leader of a French fur trading expedition in 1776 leaving the Ontario area to sell furs and get supplies for the next year. You have a choice of three forts at which you may trade. The cost of supplies and the amount you recieve for your furs will depend upon the fort you choose. You also specify what types of furs that you have to trade.
The game goes on and on until you elect to trade no longer.
Author of the program is Dan Bachor, University of Calgary, Alberta, Canada.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=69)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=84)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,15 @@
### Golf
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=71
This is a single player golf game. In other words its you against the golf course (the computer). The program asks for your handicap (maximum of 30) and your area of difficulty. You have a bag of 29 clubs plus a putter. On the course you have to contend with rough, trees, on and off fairway, sand traps, and water hazards. In addition, you can hook, slice, go out of bounds, or hit too far. On putting, you determine the potency factor (or percent of swing). Until you get the swing of the game (no pun intended), youll probably was to use a fairly high handicap.
Steve North of Creative Computing modified the original version of this game, the author of which is unknown.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=71)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=86)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Gomoko
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=74
GOMOKO or GOMOKU is a traditional game of the Orient. It is played by two people on a board of intersecting lines (19 left-to-right lines, 19 top-to-bottom lines, 361 intersections in all). Players take turns. During his turn, a player may cover one intersection with a marker; (one player uses white markers; the other player uses black markers). The object of the game is to get five adjacent markers in a row, horizontally, vertically or along either diagonal.
Unfortunately, this program does not make the computer a very good player. It does not know when you are about to win or even who has won. But some of its moves may surprise you.
The original author of this program is Peter Sessions of Peoples Computer Company.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=74)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=89)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Guess
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=75
In Program GUESS, the computer chooses a random integer between 0 and any limit you set. You must then try to guess the number the computer has chosen using the clues provided by the computer.
You should be able to guess the number in one less than the number of digits needed to represent the number in binary notation — i.e., in base 2. This ought to give you a clue as to the optimum search technique.
GUESS converted from the original program in FOCAL which appeared in the book “Computers in the Classroom” by Walt Koetke of Lexington High School, Lexington, Massachusetts.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=75)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=90)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

View File

@@ -1,7 +1,16 @@
### Gunner
As published in Basic Computer Games (1978)
https://www.atariarchives.org/basicgames/showpage.php?page=77
GUNNER allows you to adjust the fire of a field artillery weapon to hit a stationary target. You specify the number of degrees of elevation of your weapon; 45 degrees provides maximum range with values under or over 45 degrees providing less range.
You get up to five shots to destroy the enemy before he destroys you. Gun range varies between 20,000 and 60,000 yards and burst radius is 100 yards. You must specify elevation within approximately 0.2 degrees to get a hit.
Tom Kloos of the Oregon Museum of Science and Industry in Portland, Oregon originally wrote GUNNER. Extensive modifications were added by David Ahl.
---
As published in Basic Computer Games (1978):
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=77)
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=92)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html

124
42_Gunner/csharp/Program.cs Normal file
View File

@@ -0,0 +1,124 @@
namespace Gunner
{
class Program
{
static void Main(string[] args)
{
PrintIntro();
string keepPlaying = "Y";
while (keepPlaying == "Y") {
PlayGame();
Console.WriteLine("TRY AGAIN (Y OR N)");
keepPlaying = Console.ReadLine();
}
}
static void PlayGame()
{
int totalAttempts = 0;
int amountOfGames = 0;
while (amountOfGames < 4) {
int maximumRange = new Random().Next(0, 40000) + 20000;
Console.WriteLine($"MAXIMUM RANGE OF YOUR GUN IS {maximumRange} YARDS." + Environment.NewLine + Environment.NewLine + Environment.NewLine);
int distanceToTarget = (int) (maximumRange * (0.1 + 0.8 * new Random().NextDouble()));
Console.WriteLine($"DISTANCE TO THE TARGET IS {distanceToTarget} YARDS.");
(bool gameWon, int attempts) = HitTheTarget(maximumRange, distanceToTarget);
if(!gameWon) {
Console.WriteLine(Environment.NewLine + "BOOM !!!! YOU HAVE JUST BEEN DESTROYED" + Environment.NewLine +
"BY THE ENEMY." + Environment.NewLine + Environment.NewLine + Environment.NewLine
);
PrintReturnToBase();
break;
} else {
amountOfGames += 1;
totalAttempts += attempts;
Console.WriteLine($"TOTAL ROUNDS EXPENDED WERE:{totalAttempts}");
if (amountOfGames < 4) {
Console.WriteLine("THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...");
} else {
if (totalAttempts > 18) {
PrintReturnToBase();
} else {
Console.WriteLine($"NICE SHOOTING !!");
}
}
}
}
}
static (bool, int) HitTheTarget(int maximumRange, int distanceToTarget)
{
int attempts = 0;
while (attempts < 6)
{
int elevation = GetElevation();
int differenceBetweenTargetAndImpact = CalculateDifferenceBetweenTargetAndImpact(maximumRange, distanceToTarget, elevation);
if (Math.Abs(differenceBetweenTargetAndImpact) < 100)
{
Console.WriteLine($"*** TARGET DESTROYED *** {attempts} ROUNDS OF AMMUNITION EXPENDED.");
return (true, attempts);
}
else if (differenceBetweenTargetAndImpact > 100)
{
Console.WriteLine($"OVER TARGET BY {Math.Abs(differenceBetweenTargetAndImpact)} YARDS.");
}
else
{
Console.WriteLine($"SHORT OF TARGET BY {Math.Abs(differenceBetweenTargetAndImpact)} YARDS.");
}
attempts += 1;
}
return (false, attempts);
}
static int CalculateDifferenceBetweenTargetAndImpact(int maximumRange, int distanceToTarget, int elevation)
{
double weirdNumber = 2 * elevation / 57.3;
double distanceShot = maximumRange * Math.Sin(weirdNumber);
return (int)distanceShot - distanceToTarget;
}
static void PrintReturnToBase()
{
Console.WriteLine("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!");
}
static int GetElevation()
{
Console.WriteLine("ELEVATION");
int elevation = int.Parse(Console.ReadLine());
if (elevation > 89) {
Console.WriteLine("MAXIMUM ELEVATION IS 89 DEGREES");
return GetElevation();
}
if (elevation < 1) {
Console.WriteLine("MINIMUM ELEVATION IS 1 DEGREE");
return GetElevation();
}
return elevation;
}
static void PrintIntro()
{
Console.WriteLine(new String(' ', 30) + "GUNNER");
Console.WriteLine(new String(' ', 15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + Environment.NewLine + Environment.NewLine + Environment.NewLine);
Console.WriteLine("YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN");
Console.WriteLine("CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE");
Console.WriteLine("WILL PLACE A PROJECTILE ON TARGET. A HIT WITHIN 100 YARDS");
Console.WriteLine("OF THE TARGET WILL DESTROY IT." + 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>

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