Merge branch 'main' of github.com:coding-horror/basic-computer-games into main

This commit is contained in:
Brad Gilbert
2021-02-16 16:42:57 -06:00
9 changed files with 554 additions and 39 deletions

View File

@@ -1,3 +1,5 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Python](https://www.python.org/about/)
Propose using pylint and black to format python files so that it conforms to some standards

View File

@@ -24,6 +24,7 @@ cards = {
def play_game():
"""Play the game"""
cash = 100
while cash > 0:
print(f"You now have {cash} dollars\n")
@@ -63,6 +64,7 @@ def play_game():
def main():
"""Main"""
keep_playing = True
while keep_playing:
@@ -75,7 +77,7 @@ if __name__ == "__main__":
print(
"""
Acey-Ducey is played in the following manner
The dealer (computer) deals two cards face up
The dealer (computer) deals two cards face up
You have an option to be or not bet depending
on whether or not you feel the card will have
a value between the first two.

View File

@@ -1,3 +1,4 @@
"""aceyducey.py contains game code"""
########################################################
#
# Acey Ducey
@@ -31,18 +32,34 @@ DEFAULT_BANKROLL = 100
# functions
def deal_card_num():
"""Get card number"""
return random.randint(0, 12)
def get_card_name(n):
cardNames = (" 2", " 3", " 4", " 5", " 6", \
" 7", " 8", " 9", " 10", "Jack", \
"Queen", "King", "Ace")
return(cardNames[n])
def display_bankroll(b):
if bankroll > 0:
print("You now have %s dollars\n"%b)
def get_card_name(number):
"""Get card name"""
card_names = (
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
" 10",
"Jack",
"Queen",
"King",
"Ace",
)
return card_names[number]
def display_bankroll(bank_roll):
"""Print current bankroll"""
if BANK_ROLL > 0:
print("You now have %s dollars\n" % bank_roll)
# Display initial title and instructions
@@ -58,15 +75,15 @@ print("If you do not want to bet, input a 0")
# Loop for series of multiple games
keep_playing = True
while keep_playing:
KEEP_PLAYING = True
while KEEP_PLAYING:
# Initialize bankroll at start of each game
bankroll = DEFAULT_BANKROLL
display_bankroll(bankroll)
BANK_ROLL = DEFAULT_BANKROLL
display_bankroll(BANK_ROLL)
# Loop for a single round. Repeat until out of money.
while bankroll > 0:
while BANK_ROLL > 0:
# Deal out dealer cards
print("Here are your next two cards")
@@ -76,46 +93,46 @@ while keep_playing:
while dealer1 == dealer2:
dealer2 = deal_card_num()
# Organize the cards in order if they're not already
if (dealer1 >= dealer2):
(dealer1, dealer2) = (dealer2, dealer1) # Ya gotta love Python!
if dealer1 >= dealer2:
(dealer1, dealer2) = (dealer2, dealer1) # Ya gotta love Python!
# Show dealer cards to the player
# (use card name rather than internal number)
print(get_card_name(dealer1))
print(get_card_name(dealer2) + "\n")
# Get and handle player bet choice
bet_is_valid = False
while not bet_is_valid:
BET_IS_VALID = False
while not BET_IS_VALID:
curr_bet = input("What is your bet? ")
try:
curr_bet = int(curr_bet)
except:
except ValueError:
# Bad input? Just loop back up and ask again...
pass
else:
if curr_bet == 0:
bet_is_valid = True
BET_IS_VALID = True
print("Chicken!!\n")
elif curr_bet > bankroll:
elif curr_bet > BANK_ROLL:
print("Sorry, my friend but you bet too much")
print("You have only %s dollars to bet\n" % bankroll)
print("You have only %s dollars to bet\n" % BANK_ROLL)
else:
# Deal player card
bet_is_valid = True
BET_IS_VALID = True
player = deal_card_num()
print(get_card_name(player))
# Did we win?
if player > dealer1 and player < dealer2:
if dealer1 < player < dealer2:
print("You win!!!")
bankroll += curr_bet
BANK_ROLL += curr_bet
else:
print("Sorry, you lose")
bankroll -= curr_bet
BANK_ROLL -= curr_bet
# Update player on new bankroll level
display_bankroll(bankroll)
display_bankroll(BANK_ROLL)
# End of loop for a single round
print("\n\nSorry, friend but you blew your wad")
@@ -123,7 +140,7 @@ while keep_playing:
if player_response.lower() == "yes":
print()
else:
keep_playing = False
KEEP_PLAYING = False
# End of multiple game loop
@@ -187,10 +204,3 @@ print("OK Hope you had fun\n")
# get their own player card dealt).
#
########################################################

View File

@@ -0,0 +1,181 @@
/******************************************************************************
*
* Encapsulates all the state and game logic for one single game of Bagels
*
* Used by Bagels.java
*
* Jeff Jetton, 2020
*
******************************************************************************/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
public class BagelGame {
public static final String CORRECT = "FERMI FERMI FERMI";
public static final int MAX_GUESSES = 20;
enum GameState {
RUNNING,
WON,
LOST
}
private GameState state = GameState.RUNNING;
private List<Integer> secretNum;
private int guessNum = 1;
public BagelGame() {
// No-arg constructor for when you don't need to set the seed
this(new Random());
}
public BagelGame(long seed) {
// Setting the seed as a long value
this(new Random(seed));
}
public BagelGame(Random rand) {
// This is the "real" constructor, which expects an instance of
// Random to use for shuffling the digits of the secret number.
// Since the digits cannot repeat in our "number", we can't just
// pick three random 0-9 integers. Instead, we'll treat it like
// a deck of ten cards, numbered 0-9.
List<Integer> digits = new ArrayList<Integer>(10);
// The 10 specified initial allocation, not actual size,
// which is why we add rather than set each element...
for (int i = 0; i < 10; i++) {
digits.add(i);
}
// Collections offers a handy-dandy shuffle method. Normally it
// uses a fresh Random class PRNG, but we're supplying our own
// to give us controll over whether or not we set the seed
Collections.shuffle(digits, rand);
// Just take the first three digits
secretNum = digits.subList(0, 3);
}
public boolean isOver() {
return state != GameState.RUNNING;
}
public boolean isWon() {
return state == GameState.WON;
}
public int getGuessNum() {
return guessNum;
}
public String getSecretAsString() {
// Convert the secret number to a three-character string
String secretString = "";
for (int n : secretNum) {
secretString += n;
}
return secretString;
}
@Override
public String toString() {
// Quick report of game state for debugging purposes
String s = "Game is " + state + "\n";
s += "Current Guess Number: " + guessNum + "\n";
s += "Secret Number: " + secretNum;
return s;
}
public String validateGuess(String guess) {
// Checks the passed string and returns null if it's a valid guess
// (i.e., exactly three numeric characters)
// If not valid, returns an "error" string to display to user.
String error = "";
if (guess.length() == 3) {
// Correct length. Are all the characters numbers?
try {
Integer.parseInt(guess);
} catch (NumberFormatException ex) {
error = "What?";
}
if (error == "") {
// Check for unique digits by placing each character in a set
Set<Character> uniqueDigits = new HashSet<Character>();
for (int i = 0; i < guess.length(); i++){
uniqueDigits.add(guess.charAt(i));
}
if (uniqueDigits.size() != guess.length()) {
error = "Oh, I forgot to tell you that the number I have in mind\n";
error += "has no two digits the same.";
}
}
} else {
error = "Try guessing a three-digit number.";
}
return error;
}
public String makeGuess(String s) throws IllegalArgumentException {
// Processes the passed guess string (which, ideally, should be
// validated by previously calling validateGuess)
// Return a response string (PICO, FERMI, etc.) if valid
// Also sets game state accordingly (sets win state or increments
// number of guesses)
// Convert string to integer list, just to keep things civil
List<Integer> guess = new ArrayList<Integer>(3);
for (int i = 0; i < 3; i++) {
guess.add((int)s.charAt(i) - 48);
}
// Build response string...
String response = "";
// Correct digit, but in wrong place?
for (int i = 0; i < 2; i++) {
if (secretNum.get(i) == guess.get(i+1)) {
response += "PICO ";
}
if (secretNum.get(i+1) == guess.get(i)) {
response += "PICO ";
}
}
if (secretNum.get(0) == guess.get(2)) {
response += "PICO ";
}
if (secretNum.get(2) == guess.get(0)) {
response += "PICO ";
}
// Correct digits in right place?
for (int i = 0; i < 3; i++) {
if (secretNum.get(i) == guess.get(i)) {
response += "FERMI ";
}
}
// Nothin' right?
if (response == "") {
response = "BAGELS";
}
// Get rid of any space that might now be at the end
response = response.trim();
// If correct, change state
if (response.equals(CORRECT)) {
state = GameState.WON;
} else {
// If not, increment guess counter and check for game over
guessNum++;
if (guessNum > MAX_GUESSES) {
state = GameState.LOST;
}
}
return response;
}
}

133
05 Bagels/java/Bagels.java Normal file
View File

@@ -0,0 +1,133 @@
/******************************************************************************
*
* Bagels
*
* From: BASIC Computer Games (1978)
* Edited by David H. Ahl
*
* "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, you'll 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 and 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."
*
* Java port by Jeff Jetton, 2020, based on an earlier Python port
*
******************************************************************************/
import java.util.Scanner;
public class Bagels {
public static void main(String[] args) {
int gamesWon = 0;
// Intro text
System.out.println("\n\n Bagels");
System.out.println("Creative Computing Morristown, New Jersey");
System.out.println("\n\n");
System.out.print("Would you like the rules (Yes or No)? ");
// Need instructions?
Scanner scan = new Scanner(System.in);
String s = scan.nextLine();
if (s.length() == 0 || s.toUpperCase().charAt(0) != 'N') {
System.out.println();
System.out.println("I am thinking of a three-digit number. Try to guess");
System.out.println("my number and I will give you clues as follows:");
System.out.println(" PICO - One digit correct but in the wrong position");
System.out.println(" FERMI - One digit correct and in the right position");
System.out.println(" BAGELS - No digits correct");
}
// Loop for playing multiple games
boolean stillPlaying = true;
while(stillPlaying) {
// Set up a new game
BagelGame game = new BagelGame();
System.out.println("\nO.K. I have a number in mind.");
// Loop guess and responsses until game is over
while (!game.isOver()) {
String guess = getValidGuess(game);
String response = game.makeGuess(guess);
// Don't print a response if the game is won
if (!game.isWon()) {
System.out.println(response);
}
}
// Game is over. But did we win or lose?
if (game.isWon()) {
System.out.println("You got it!!!\n");
gamesWon++;
} else {
System.out.println("Oh well");
System.out.print("That's " + BagelGame.MAX_GUESSES + " guesses. ");
System.out.println("My number was " + game.getSecretAsString());
}
stillPlaying = getReplayResponse();
}
// Print goodbye message
if (gamesWon > 0) {
System.out.println("\nA " + gamesWon + " point Bagels buff!!");
}
System.out.println("Hope you had fun. Bye.\n");
}
private static String getValidGuess(BagelGame game) {
// Keep asking for a guess until valid
Scanner scan = new Scanner(System.in);
boolean valid = false;
String guess = "";
String error;
while (!valid) {
System.out.print("Guess # " + game.getGuessNum() + " ? ");
guess = scan.nextLine().trim();
error = game.validateGuess(guess);
if (error == "") {
valid = true;
} else {
System.out.println(error);
}
}
return guess;
}
private static boolean getReplayResponse() {
// keep asking for response until valid
Scanner scan = new Scanner(System.in);
// Keep looping until a non-zero-length string is entered
while (true) {
System.out.print("Play again (Yes or No)? ");
String response = scan.nextLine().trim();
if (response.length() > 0) {
return response.toUpperCase().charAt(0) == 'Y';
}
}
}
}

View File

@@ -0,0 +1,17 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
## Conversion
Not a difficult conversion - but a chance to throw in a few ways
Perl makes life easy.
* To get the sub permission which is a random location in the g x g x g grid we can use:
* assigning multiple variables in list form ($a,$b,$c) = (?,?,?)
* where the list on the right hand side is generated with a map function
* We use ternarys to generate the message if you miss the sub.
* We use join to stitch the pieces of the string together.
* If we have a ternary where we don't want to return anything we return an empty list rather than an empty string - if you return the latter you still get the padding spaces.

View File

@@ -0,0 +1,52 @@
#!/usr/bin/perl
use strict;
use warnings;
print ' Depth Charge
Creative Computing Morristown, New Jersey
Depth Charge Game
Dimensions of Search Area? ';
my $g = <STDIN>;
my $n = int( log($g) / log 2 ) + 1;
print '
You are the captain of the Destroyer USS Computer
an enemy sub has been causing you trouble. Your
mission is to destroy it. You have ',$n,' shots.
Specify depth charge explosion point with a
trio of number -- the first two are the surface
co-ordinates; the third is the depth.
';
while(1) { ## Repeat until we say no....
print "\nGood luck!\n\n";
my ($a,$b,$c) = map { int rand $g } 1..3; ## Get the location
my $hit = 0; ## Keep track if we have won yet!
foreach ( 1..$n ) {
print "\nTrial # $_ ? ";
my ( $x, $y, $z ) = split m{\D+}, <STDIN>;
if( $x==$a && $y==$b && $z==$c ) {
$hit = 1; ## We have won
print "\n\nB O O M ! ! You found it in $_ tries!\n";
last;
}
print join q( ), 'Sonar reports show was',
$y < $b ? 'South' : $y > $b ? 'North' : (),
$x < $a ? 'West' : $x > $a ? 'East' : (),
$x == $a && $y == $b ? () : 'and' ,
$z < $c ? 'too high' : $z > $c ? 'too low' : 'depth OK',
".\n";
}
## Only show message if we haven't won...
print "\nYou have been torpedoed! Abandon ship!\nThe submarine was at $a, $b, $c\n" unless $hit;
print "\n\nAnother game (Y or N)? ";
last unless <STDIN> =~ m{Y}i; ## Y or y not typed so leave loop
}
## Say good bye
print "OK. Hope you enjoyed yourself.\n\n";

96
41 Guess/python/guess.py Normal file
View File

@@ -0,0 +1,96 @@
########################################################
#
# Guess
#
# From: Basic Computer Games (1978)
#
# "In program Guess, the computer chooses a random
# integer between 0 and any limit and any limit you
# set. You must then try to guess the number the
# computer has choosen using the clues provideed 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,
# Massaschusetts.
#
########################################################
# Altough the introduction says that the computer chooses
# a number between 0 and any limit, it actually chooses
# a number between 1 and any limit. This due to the fact that
# for computing the number of digits the limit has in binary
# representation, it has to use log.
from math import log
from random import random
def insert_whitespaces():
print("\n\n\n\n\n")
def limit_set():
print(" Guess")
print("Creative Computing Morristown, New Jersey")
print("\n\n\n")
print("This is a number guessing game. I'll think")
print("of a number between 1 and any limit you want.\n")
print("Then you have to guess what it is\n")
print("What limit do you want?")
limit = int(input())
while limit <= 0:
print("Please insert a number greater or equal to 1")
limit = int(input())
# limit_goal = Number of digits "limit" in binary has
limit_goal = int((log(limit) / log(2)) + 1)
return limit, limit_goal
limit, limit_goal = limit_set()
while True:
guess_count = 1
still_guessing = True
won = False
my_guess = int(limit * random() + 1)
print("I'm thinking of a number between 1 and {}".format(limit))
print("Now you try to guess what it is.")
while still_guessing:
n = int(input())
if n < 0:
break
insert_whitespaces()
if n < my_guess:
print("Too low. Try a bigger answer")
guess_count += 1
elif n > my_guess:
print("Too high. Try a smaller answer")
guess_count += 1
else:
print("That's it! You got it in {} tries".format(guess_count))
won = True
still_guessing = False
if won:
if guess_count < limit_goal:
print("Very good.")
elif guess_count == limit_goal:
print("Good.")
else:
print("You should have been able to get it in only {}".format(limit_goal))
insert_whitespaces()
else:
insert_whitespaces()
limit, limit_goal = limit_set()

View File

@@ -0,0 +1,22 @@
print(tab(30), "SINE WAVE");
print(tab(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
print("\n\n\n\n");
// REMARKABLE PROGRAM BY DAVID AHL
// Transliterated to Javascript by Les Orchard <me@lmorchard.com>
let toggleWord = true;
for (let step = 0; step < 40; step += 0.25) {
let indent = Math.floor(26 + 25 * Math.sin(step));
print(tab(indent), toggleWord ? "CREATIVE" : "COMPUTING");
toggleWord = !toggleWord;
}
function print(...messages) {
console.log(messages.join(" "));
}
function tab(count) {
return " ".repeat(count);
}