diff --git a/01_Acey_Ducey/README.md b/01_Acey_Ducey/README.md index 3434eb8e..77ff8621 100644 --- a/01_Acey_Ducey/README.md +++ b/01_Acey_Ducey/README.md @@ -15,4 +15,5 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html -A Common Lisp port is [here](https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp). +#### External Links + - Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp diff --git a/01_Acey_Ducey/java/src/AceyDucey.java b/01_Acey_Ducey/java/src/AceyDucey.java index 43fc9950..d53c0d05 100644 --- a/01_Acey_Ducey/java/src/AceyDucey.java +++ b/01_Acey_Ducey/java/src/AceyDucey.java @@ -167,6 +167,6 @@ public class AceyDucey { System.out.println("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING"); System.out.println("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE"); System.out.println("A VALUE BETWEEN THE FIRST TWO."); - System.out.println("IF YOU DO NOT WANT TO BET, INPUT A 0"); + System.out.println("IF YOU DO NOT WANT TO BET, INPUT: 0"); } } \ No newline at end of file diff --git a/01_Acey_Ducey/perl/aceyducey.pl b/01_Acey_Ducey/perl/aceyducey.pl index e2de13df..d4dfb2c2 100755 --- a/01_Acey_Ducey/perl/aceyducey.pl +++ b/01_Acey_Ducey/perl/aceyducey.pl @@ -28,17 +28,15 @@ If you do not want to bet, input a 0. If you want to quit, input a -1. END_INSTRUCTIONS -my @cards = (1 .. 13); # That is, Ace through King. +my @cards = ( 1 .. 13 ); # That is, Ace through King. my $keepPlaying = 1; GAME: -while ($keepPlaying) -{ - my $playerBalance = 100; # The player starts with $100 +while ($keepPlaying) { + my $playerBalance = 100; # The player starts with $100 - HAND: - while (1) - { + HAND: + while (1) { print "\nYou now have $playerBalance dollars.\n\n"; # We'll create a new array that is a shuffled version of the deck. @@ -48,19 +46,17 @@ while ($keepPlaying) # that those will be unique. This way we don't have to keep drawing # if we get, say, two queens. We sort them as we pull them to make # sure that the first card is lower than the second one. - my ($firstCard, $secondCard) = sort { $a <=> $b } @shuffledDeck[ 0 .. 1 ]; + my ( $firstCard, $secondCard ) = sort { $a <=> $b } @shuffledDeck[ 0 .. 1 ]; print "I drew ", nameOfCard($firstCard), " and ", nameOfCard($secondCard), ".\n"; my $bet = getValidBet($playerBalance); - if ($bet == 0) - { + if ( $bet == 0 ) { print "Chicken!\n\n"; next HAND; } - if ($bet < 0) - { + if ( $bet < 0 ) { last GAME; } @@ -72,19 +68,16 @@ while ($keepPlaying) print "I drew ", nameOfCard($thirdCard), "!\n"; - if (($firstCard < $thirdCard) && ($thirdCard < $secondCard)) - { + if ( ( $firstCard < $thirdCard ) && ( $thirdCard < $secondCard ) ) { print "You win!\n\n"; $playerBalance += $bet; } - else - { + else { print "You lose!\n\n"; $playerBalance -= $bet; } - if ($playerBalance <= 0) - { + if ( $playerBalance <= 0 ) { print "Sorry, buddy, you blew your wad!\n\n"; last HAND; } @@ -96,49 +89,43 @@ while ($keepPlaying) print "Thanks for playing!\n"; ############### -sub getValidBet -{ +sub getValidBet { my $maxBet = shift; - print "\nWhat's your bet? "; - - my $input = ; - chomp $input; - - # This regular expression will validate that the player entered an integer. - # The !~ match operate *negates* the match, so if the player did NOT enter - # an integer, they'll be given an error and prompted again. - if ($input !~ /^ # Match the beginning of the string - [+-]? # Optional plus or minus... - \d+ # followed by one more more digits... - $ # and then the end of the string - /x # The x modifier ignores whitespace in this regex... - ) + INPUT: { - print "Sorry, numbers only!\n"; - $input = getValidBet($maxBet); - } + print "\nWhat's your bet? "; - if ($input > $maxBet) - { - print "Sorry, my friend, you can't bet more money than you have.\n"; - print "You only have $maxBet dollars to spend!\n"; - $input = getValidBet($maxBet); - } + chomp( my $input = ); - if ($input != int($input)) - { - print "Sorry, you must bet in whole dollars. No change!\n"; - $input = getValidBet($maxBet); - } + # This regular expression will validate that the player entered an integer. + # The !~ match operate *negates* the match, so if the player did NOT enter + # an integer, they'll be given an error and prompted again. + if ( + $input !~ /^ # Match the beginning of the string + [+-]? # Optional plus or minus... + \d+ # followed by one more more digits... + $ # and then the end of the string + /x # The x modifier ignores whitespace in this regex... + ) + { + print "Sorry, numbers only!\n"; + redo INPUT; + } - return $input; + if ( $input > $maxBet ) { + print "Sorry, my friend, you can't bet more money than you have.\n"; + print "You only have $maxBet dollars to spend!\n"; + redo INPUT; + } + + return $input; + } } # Since arrays in Perl are 0-based, we need to convert the value that we drew from # the array to its proper position in the deck. -sub nameOfCard -{ +sub nameOfCard { my $value = shift; # Note that the Joker isn't used in this game, but since arrays in Perl are @@ -150,25 +137,21 @@ sub nameOfCard return $cardlist[$value]; } -sub promptUserToKeepPlaying -{ - print "Try again (Y/N)? "; - my $input = ; - chomp $input; +sub promptUserToKeepPlaying { + YESNO: + { + print "Try again (Y/N)? "; - my $keepGoing; - if (uc($input) eq 'Y') - { - $keepGoing = 1; - } - elsif (uc($input) eq 'N') - { - $keepGoing = 0; - } - else - { - $keepGoing = promptUserToKeepPlaying(); - } + chomp( my $input = uc ); - return $keepGoing; + if ( $input eq 'Y' ) { + return 1; + } + elsif ( $input eq 'N' ) { + return 0; + } + else { + redo YESNO; + } + } } diff --git a/02_Amazing/perl/amazing.pl b/02_Amazing/perl/amazing.pl new file mode 100755 index 00000000..c7cb5f44 --- /dev/null +++ b/02_Amazing/perl/amazing.pl @@ -0,0 +1,159 @@ +#! /usr/bin/perl +use strict; +use warnings; + +# Translated from BASIC by Alex Kapranoff + +use feature qw/say/; + +# width and height of the maze +my ($width, $height) = input_dimensions(); + +# wall masks for all cells +my @walls; + +# flags of previous visitation for all cells +my %is_visited; + +# was the path out of the maze found? +my $path_found = 0; + +# column of entry to the maze in the top line +my $entry_col = int(rand($width)); + +# cell coordinates for traversal +my $col = $entry_col; +my $row = 0; + +$is_visited{$row, $col} = 1; + +# looping until we visit every cell +while (keys %is_visited < $width * $height) { + if (my @dirs = get_possible_directions()) { + my $dir = $dirs[rand @dirs]; + + # modify current cell wall if needed + $walls[$row]->[$col] |= $dir->[2]; + + # move the position + $row += $dir->[0]; + $col += $dir->[1]; + + # we found the exit! + if ($row == $height) { + $path_found = 1; + --$row; + + if ($walls[$row]->[$col] == 1) { + ($row, $col) = get_next_branch(0, 0); + } + } + else { + # modify the new cell wall if needed + $walls[$row]->[$col] |= $dir->[3]; + + $is_visited{$row, $col} = 1; + } + } + else { + ($row, $col) = get_next_branch($row, $col); + } +} + +unless ($path_found) { + $walls[-1]->[rand $width] |= 1; +} + +print_maze(); + +sub input_dimensions { + # Print the banner and returns the dimensions as two integers > 1. + # The integers are parsed from the first line of standard input. + say ' ' x 28, 'AMAZING PROGRAM'; + say ' ' x 15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'; + print "\n" x 4; + + my ($w, $h) = (0, 0); + + while ($w <= 1 || $h <= 1) { + print 'WHAT ARE YOUR WIDTH AND LENGTH? '; + + ($w, $h) = =~ / \d+ /xg; + + if ($w < 1 || $h < 1) { + say "MEANINGLESS DIMENSIONS. TRY AGAIN." + } + } + + print "\n" x 4; + + return ($w, $h); +} + +sub get_possible_directions { + # Returns a list of all directions that are available to go to + # from the current coordinates. "Down" is available on the last line + # until we go there once and mark it as the path through the maze. + # + # Each returned direction element contains changes to the coordindates and to + # the wall masks of the previous and next cell after the move. + + my @rv; + # up + if ($row > 0 && !$is_visited{$row - 1, $col}) { + push @rv, [-1, 0, 0, 1]; + } + # left + if ($col > 0 && !$is_visited{$row, $col - 1}) { + push @rv, [0, -1, 0, 2]; + } + # right + if ($col < $width - 1 && !$is_visited{$row, $col + 1}) { + push @rv, [0, 1, 2, 0]; + } + # down + if ($row < $height - 1 && !$is_visited{$row + 1, $col} + || $row == $height - 1 && !$path_found + ) { + push @rv, [1, 0, 1, 0]; + } + + return @rv; +} + +sub get_next_branch { + # Returns the cell coordinates to start a new maze branch from. + # It looks for a visited cell starting from passed position and + # going down in the natural traversal order incrementing column and + # rows with a rollover to start at the bottom right corner. + my ($y, $x) = @_; + do { + if ($x < $width - 1) { + ++$x; + } elsif ($y < $height - 1) { + ($y, $x) = ($y + 1, 0); + } else { + ($y, $x) = (0, 0); + } + } while (!$is_visited{$y, $x}); + + return ($y, $x); +} + +sub print_maze { + # Print the full maze based on wall masks. + # For each cell, we mark the absense of the wall to the right with + # bit 2 and the absense of the wall down with bit 1. Full table: + # 0 -> both walls are present + # 1 -> wall down is absent + # 2 -> wall to the right is absent + # 3 -> both walls are absent + say join('.', '', map { $_ == $entry_col ? ' ' : '--' } 0 .. $width - 1), '.'; + + for my $row (@walls) { + say join(' ', map { $_ & 2 ? ' ' : 'I' } 0, @$row); + say join(':', '', map { $_ & 1 ? ' ' : '--' } @$row), '.'; + } + + return; +} diff --git a/24_Chemist/README.md b/24_Chemist/README.md index 95db3f66..b93c1431 100644 --- a/24_Chemist/README.md +++ b/24_Chemist/README.md @@ -13,4 +13,5 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html -[Conversion to C](https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c) +#### External Links + - C: https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c diff --git a/43_Hammurabi/README.md b/43_Hammurabi/README.md index c92b7ee3..c02219b1 100644 --- a/43_Hammurabi/README.md +++ b/43_Hammurabi/README.md @@ -21,6 +21,6 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html -[Port to C language](https://github.com/beyonddream/hamurabi) - -[Port to Rust language](https://github.com/beyonddream/hamurabi.rs) +#### External Links + - C: https://github.com/beyonddream/hamurabi + - Rust: https://github.com/beyonddream/hamurabi.rs diff --git a/44_Hangman/perl/hangman.pl b/44_Hangman/perl/hangman.pl new file mode 100755 index 00000000..45a6da9b --- /dev/null +++ b/44_Hangman/perl/hangman.pl @@ -0,0 +1,223 @@ +#!/usr/bin/perl + +use strict; +use warnings; + + +# global variables defined here + +my(@WORDS) = qw( + GUM SIN FOR CRY LUG BYE FLY + UGLY EACH FROM WORK TALK WITH SELF + PIZZA THING FEIGN FIEND ELBOW FAULT DIRTY + BUDGET SPIRIT QUAINT MAIDEN ESCORT PICKAX + EXAMPLE TENSION QUININE KIDNEY REPLICA SLEEPER + TRIANGLE KANGAROO MAHOGANY SERGEANT SEQUENCE + MOUSTACHE DANGEROUS SCIENTIST DIFFERENT QUIESCENT + MAGISTRATE ERRONEOUSLY LOUDSPEAKER PHYTOTOXIC + MATRIMONIAL PARASYMPATHOMIMETIC THIGMOTROPISM +); +my(@PIC,$board,@guessedLetters,$guessCount,$hangCount); +my(%GUESSED); + +# Subroutines defined here. + +# init_variables: initialize all of the variables needed +# (this covers lines 50-90 in the original BASIC program) + +sub init_variables { + @guessedLetters = (); + @PIC = ( + 'XXXXXXX ', + 'X X ', + 'X ', + 'X ', + 'X ', + 'X ', + 'X ', + 'X ', + 'X ', + 'X ', + 'X ', + 'X ', + ); + $guessCount = 0; %GUESSED = (); + $hangCount = 0; +} + +# addchar: given a row & column, put the specified char in that place in @PIC +sub addchar { + my($row,$col, $c) = @_; + + substr($PIC[$row],$col,1) = $c; +} + + +# main code starts here + +print ' 'x31; print "Hangman\n"; +print ' 'x14; print "Creative Computing Morristown, New Jersey\n\n\n\n"; + +# an iteration of the PLAY block is one complete game. +# There is a continue block that will ask if the user +# wants to play another game. + +PLAY: while (1) { + + init_variables(); + # Any words left? + if (@WORDS == 0) { + print "You did all the words!\n"; + last PLAY; + } + # splice a random word out of the @WORDS array + my($thisWord) = splice(@WORDS, int(rand(scalar @WORDS)),1); + # $board is the "game board" of the filled-out word + # that the user is working on + $board = '.'x(length $thisWord); + + # GUESS loop is run for every time the user guesses a letter + GUESS: while(1) { + print "Here are the letters you used:\n"; + printf("%s\n", join(',',@guessedLetters)); + printf("\n\n%s\n", $board); + + print "What is your guess for a letter ? "; + chomp(my $guess = ); + # The %GUESSED hash allows us to quickly identify + # letters that have already been guessed + if ($GUESSED{lc $guess}) { + print "You guessed that letter before!\n\n"; + redo GUESS; + } + + # save the guessed letter + push @guessedLetters, $guess; + $GUESSED{lc $guess} = 1; + ++$guessCount; + + # now look for the letter in the $thisWord var + # and put it into the $board var wherever it + # shows up. $foundLetter is a flag that indicates + # whether or not the letter is found. + my $foundLetter = 0; + for (my $i = 0; $i < length $thisWord; ++$i) { + if (lc substr($thisWord,$i,1) eq lc $guess) { + $foundLetter = 1; + substr($board, $i, 1) = substr($thisWord, $i, 1); + } + } + + # The user found a letter in the solution! + if ($foundLetter) { + + # Are there any '.' chars left in the board? + if (index($board, '.') < 0) { + print "You found the word!\n\n"; + } else { + printf("%s\n\n", $board); + print "What is your guess for the word ? "; + chomp(my $guessword = ); + if (lc $thisWord ne lc $guessword) { + print "Wrong. Try another letter.\n"; + # Go to the next iteration of the GUESS loop + next GUESS; + } + printf("Right! It took you %d %s!\n", $guessCount, ($guessCount == 1 ? 'guess' : 'guesses')); + } + # At this point the user has discovered the word and won. + # This "next" statement takes execution down to the + # continue block for the PLAY loop; + next PLAY; + + } else { # didn't find a letter + + ++$hangCount; + print "\n\n\nSorry, that letter isn't in the word.\n"; + + # The addchar() calls in the block below piece together the + # hangman graphic, depending on how many wrong letters + # the user has. + if ($hangCount == 1) { + print "First, we draw a head\n"; + addchar(2,5,"-");addchar(2,6,"-");addchar(2,7,"-"); + addchar(3,4,"("); addchar(3,5,"."); addchar(3,7,"."); addchar(3,8,")"); + addchar(4,5,"-");addchar(4,6,"-");addchar(4,7,"-"); + } + if ($hangCount == 2) { + print "Now we draw a body.\n"; + for (5 .. 8) { + addchar($_, 6, "X"); + } + } + if ($hangCount == 3) { + print "Next we draw an arm.\n"; + for (3 .. 6) { + addchar($_, $_-1, "\\"); + } + } + if ($hangCount == 4) { + print "This time it's the other arm.\n"; + addchar(3,10, "/"); + addchar(4, 9, "/"); + addchar(5, 8, "/"); + addchar(6, 7, "/"); + } + if ($hangCount == 5) { + print "Now, let's draw the right leg.\n"; + addchar( 9,5, "/"); + addchar(10,4, "/"); + } + if ($hangCount == 6) { + print "This time we draw the left leg.\n"; + addchar(9,7,"\\"); + addchar(10,8,"\\"); + } + if ($hangCount == 7) { + print "Now we put up a hand.\n"; + addchar(2,10,"\\"); + } + if ($hangCount == 8) { + print "Next the other hand.\n"; + addchar(2,2,"/"); + } + if ($hangCount == 9) { + print "Now we draw one foot\n"; + addchar(11,9,"\\"); + addchar(11,10, "-"); + } + if ($hangCount == 10) { + print "Here's the other foot -- you're hung!!\n"; + addchar(11,2,"-"); + addchar(11,3, "/"); + } + + printf("$_\n") for @PIC; + print "\n\n"; + + # Next guess if the user has not lost + if ($hangCount < 10) { + next GUESS; + } + + printf("Sorry, you lose. The word was %s\n", $thisWord); + next PLAY; + + } # didn't find a letter block + } # GUESS block +} # PLAY block + +# This block is reached either by the player winning (see the "next PLAY") +# statement) or by the user losing (as the PLAY block is complete and +# execution naturally comes to this continue block). +continue { + print "Want another word ? "; + chomp(my $in = ); + if ($in !~ m/^y/i) { + # Exit the PLAY loop + print "\nIt's been fun! Bye for now.\n\n"; + last PLAY; + } + # At this point execution goes to the start of the PLAY block, + # meaning a new game +} diff --git a/81_Splat/java/README.md b/81_Splat/java/README.md index 51edd8d4..b8998fa6 100644 --- a/81_Splat/java/README.md +++ b/81_Splat/java/README.md @@ -1,3 +1,3 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) -Conversion to [Oracle Java](https://openjdk.java.net/) +Conversion to [Java](https://openjdk.java.net/) diff --git a/81_Splat/java/src/Splat.java b/81_Splat/java/src/Splat.java new file mode 100644 index 00000000..b76c82ff --- /dev/null +++ b/81_Splat/java/src/Splat.java @@ -0,0 +1,348 @@ +import java.util.*; + +/** + * SPLAT simulates a parachute jump in which you try to open your parachute at the last possible moment without going + * splat! You may select your own terminal velocity or let the computer do it for you. You many also select the + * acceleration due to gravity or, again, let the computer do it in which case you might wind up on any of eight + * planets (out to Neptune), the moon, or the sun. + *

+ * The computer then tells you the height you’re jumping from and asks for the seconds of free fall. It then divides + * your free fall time into eight intervals and gives you progress reports on your way down. The computer also keeps + * track of all prior jumps in the array A and lets you know how you compared with previous successful jumps. If you + * want to recall information from previous runs, then you should store array A in a disk or take file and read it + * before each run. + *

+ * John Yegge created this program while at the Oak Ridge Associated Universities. + *

+ * Ported from BASIC by jason plumb (@breedx2) + *

+ */ +public class Splat { + private static final Random random = new Random(); + private final Scanner scanner = new Scanner(System.in); + private final List pastSuccessfulJumpDistances = new ArrayList<>(); + + public static void main(String[] args) { + new Splat().run(); + } + + public void run() { + showIntroduction(); + + while (true) { + + InitialJumpConditions initial = buildInitialConditions(); + + System.out.println(); + System.out.printf(" ALTITUDE = %d FT\n", initial.getAltitude()); + System.out.printf(" TERM. VELOCITY = %.2f FT/SEC +/-5%%\n", initial.getOriginalTerminalVelocity()); + System.out.printf(" ACCELERATION = %.2f FT/SEC/SEC +/-5%%\n", initial.getOriginalAcceleration()); + + System.out.println("SET THE TIMER FOR YOUR FREEFALL."); + System.out.print("HOW MANY SECONDS "); + float freefallTime = scanner.nextFloat(); + System.out.println("HERE WE GO.\n"); + System.out.println("TIME (SEC) DIST TO FALL (FT)"); + System.out.println("========== ================="); + + JumpResult jump = executeJump(initial, freefallTime); + showJumpResults(initial, jump); + + if (!playAgain()) { + System.out.println("SSSSSSSSSS."); + return; + } + } + } + + private void showIntroduction() { + System.out.printf("%33s%s\n", " ", "SPLAT"); + System.out.printf("%15s%s\n", " ", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + System.out.print("\n\n\n"); + System.out.println("WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE"); + System.out.println("JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE"); + System.out.println("MOMENT WITHOUT GOING SPLAT."); + } + + private InitialJumpConditions buildInitialConditions() { + System.out.print("\n\n"); + float terminalVelocity = promptTerminalVelocity(); + float acceleration = promptGravitationalAcceleration(); + return InitialJumpConditions.create(terminalVelocity, acceleration); + } + + private float promptTerminalVelocity() { + if (askYesNo("SELECT YOUR OWN TERMINAL VELOCITY")) { + System.out.print("WHAT TERMINAL VELOCITY (MI/HR) "); + return mphToFeetPerSec(scanner.nextFloat()); + } + float terminalVelocity = (int) (1000 * random.nextFloat()); + System.out.printf("OK. TERMINAL VELOCITY = %.2f MI/HR\n", terminalVelocity); + return mphToFeetPerSec(terminalVelocity); + } + + private float promptGravitationalAcceleration() { + if (askYesNo("WANT TO SELECT ACCELERATION DUE TO GRAVITY")) { + System.out.print("WHAT ACCELERATION (FT/SEC/SEC) "); + return scanner.nextFloat(); + } + return chooseRandomAcceleration(); + } + + private JumpResult executeJump(InitialJumpConditions initial, float chuteOpenTime) { + JumpResult jump = new JumpResult(initial.getAltitude()); + for (float time = 0.0f; time < chuteOpenTime; time += chuteOpenTime / 8) { + if (!jump.hasReachedTerminalVelocity() && time > initial.getTimeOfTerminalAccelerationReached()) { + jump.setReachedTerminalVelocity(); + System.out.printf("TERMINAL VELOCITY REACHED AT T PLUS %f SECONDS.\n", initial.getTimeOfTerminalAccelerationReached()); + } + float newDistance = computeDistance(initial, time, jump.hasReachedTerminalVelocity()); + jump.setDistance(newDistance); + + if (jump.isSplat()) { + return jump; + } + System.out.printf("%10.2f %f\n", time, jump.getDistance()); + } + return jump; + } + + private float computeDistance(InitialJumpConditions initial, float i, boolean hasReachedTerminalVelocity) { + final float V = initial.getTerminalVelocity(); + final float A = initial.getAcceleration(); + if (hasReachedTerminalVelocity) { + return initial.getAltitude() - ((V * V / (2 * A)) + (V * (i - (V / A)))); + } + return initial.getAltitude() - ((A / 2) * i * i); + } + + private void showJumpResults(InitialJumpConditions initial, JumpResult jump) { + if (jump.isSplat()) { + showSplatMessage(initial, jump); + showCleverSplatMessage(); + return; + } + System.out.println("CHUTE OPEN"); + int worseJumpCount = countWorseHistoricalJumps(jump); + int successfulJumpCt = pastSuccessfulJumpDistances.size(); + pastSuccessfulJumpDistances.add(jump.getDistance()); + + if (pastSuccessfulJumpDistances.size() <= 2) { + List ordinals = Arrays.asList("1ST", "2ND", "3RD"); + System.out.printf("AMAZING!!! NOT BAD FOR YOUR %s SUCCESSFUL JUMP!!!\n", ordinals.get(successfulJumpCt)); + return; + } + + int betterThanCount = successfulJumpCt - worseJumpCount; + if (betterThanCount <= 0.1 * successfulJumpCt) { + System.out.printf("WOW! THAT'S SOME JUMPING. OF THE %d SUCCESSFUL JUMPS\n", successfulJumpCt); + System.out.printf("BEFORE YOURS, ONLY %d OPENED THEIR CHUTES LOWER THAN\n", betterThanCount); + System.out.println("YOU DID."); + } else if (betterThanCount <= 0.25 * successfulJumpCt) { + System.out.printf("PRETTY GOOD! %d SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n", successfulJumpCt); + System.out.printf("%d OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\n", betterThanCount); + System.out.println("OPENED."); + } else if (betterThanCount <= 0.5 * successfulJumpCt) { + System.out.printf("NOT BAD. THERE HAVE BEEN %d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt); + System.out.printf("YOU WERE BEATEN OUT BY %d OF THEM.\n", betterThanCount); + } else if (betterThanCount <= 0.75 * successfulJumpCt) { + System.out.printf("CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY %d IN THE\n", betterThanCount); + System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt); + } else if (betterThanCount <= -0.9 * successfulJumpCt) { + System.out.println("HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE"); + System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN %d JUMPS\n", successfulJumpCt, worseJumpCount); + System.out.println("BETTER THAN THE WORST. SHAPE UP!!!\n"); + } else { + System.out.printf("HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. %d SUCCESSFUL\n", successfulJumpCt); + System.out.printf("JUMPS BEFORE YOURS AND YOU CAME IN NUMBER %d! GET WITH IT!\n", betterThanCount); + } + } + + private void showSplatMessage(InitialJumpConditions initial, JumpResult jump) { + double timeOfSplat = computeTimeOfSplat(initial, jump); + System.out.printf("%10.2f SPLAT\n", timeOfSplat); + } + + /** + * Returns the number of jumps for which this jump was better + */ + private double computeTimeOfSplat(InitialJumpConditions initial, JumpResult jump) { + final float V = initial.getTerminalVelocity(); + final float A = initial.getAcceleration(); + if (jump.hasReachedTerminalVelocity()) { + return (V / A) + ((initial.getAltitude() - (V * V / (2 * A))) / V); + } + return Math.sqrt(2 * initial.getAltitude() / A); + } + + private int countWorseHistoricalJumps(JumpResult jump) { + return (int) pastSuccessfulJumpDistances.stream() + .filter(distance -> jump.getDistance() < distance) + .count(); + } + + private void showCleverSplatMessage() { + List messages = Arrays.asList( + "REQUIESCAT IN PACE.", + "MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.", + "REST IN PEACE.", + "SON-OF-A-GUN.", + "#$%&&%!$", + "A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.", + "HMMM. SHOULD HAVE PICKED A SHORTER TIME.", + "MUTTER. MUTTER. MUTTER.", + "PUSHING UP DAISIES.", + "EASY COME, EASY GO." + ); + System.out.println(messages.get(random.nextInt(10))); + } + + private boolean playAgain() { + if (askYesNo("DO YOU WANT TO PLAY AGAIN ")) { + return true; + } + return askYesNo("PLEASE"); + } + + private float mphToFeetPerSec(float speed) { + return speed * (5280.0f / 3600.0f); + } + + private boolean askYesNo(String prompt) { + System.out.printf("%s (YES OR NO) ", prompt); + while (true) { + String answer = scanner.next(); + switch (answer) { + case "YES": + return true; + case "NO": + return false; + default: + System.out.print("YES OR NO "); + } + } + } + + private float chooseRandomAcceleration() { + Planet planet = Planet.pickRandom(); + System.out.printf("%s %s. ACCELERATION=%.2f FT/SEC/SEC.\n", planet.getMessage(), planet.name(), planet.getAcceleration()); + return planet.getAcceleration(); + } + + enum Planet { + MERCURY("FINE. YOU'RE ON", 12.2f), + VENUS("ALL RIGHT. YOU'RE ON", 28.3f), + EARTH("THEN YOU'RE ON", 32.16f), + MOON("FINE. YOU'RE ON THE", 5.15f), + MARS("ALL RIGHT. YOU'RE ON", 12.5f), + JUPITER("THEN YOU'RE ON", 85.2f), + SATURN("FINE. YOU'RE ON", 37.6f), + URANUS("ALL RIGHT. YOU'RE ON", 33.8f), + NEPTUNE("THEN YOU'RE ON", 39.6f), + SUN("FINE. YOU'RE ON THE", 896.0f); + + private static final Random random = new Random(); + private final String message; + private final float acceleration; + + Planet(String message, float acceleration) { + this.message = message; + this.acceleration = acceleration; + } + + static Planet pickRandom() { + return values()[random.nextInt(Planet.values().length)]; + } + + String getMessage() { + return message; + } + + float getAcceleration() { + return acceleration; + } + } + + // Mutable + static class JumpResult { + private boolean reachedTerminalVelocity = false; + private float distance; // from the ground + + public JumpResult(float distance) { + this.distance = distance; + } + + boolean isSplat() { + return distance <= 0; + } + + boolean hasReachedTerminalVelocity() { + return reachedTerminalVelocity; + } + + float getDistance() { + return distance; + } + + void setDistance(float distance) { + this.distance = distance; + } + + void setReachedTerminalVelocity() { + reachedTerminalVelocity = true; + } + } + + // Immutable + static class InitialJumpConditions { + private final float originalTerminalVelocity; + private final float originalAcceleration; + private final float terminalVelocity; + private final float acceleration; + private final int altitude; + + private InitialJumpConditions(float originalTerminalVelocity, float originalAcceleration, + float terminalVelocity, float acceleration, int altitude) { + this.originalTerminalVelocity = originalTerminalVelocity; + this.originalAcceleration = originalAcceleration; + this.terminalVelocity = terminalVelocity; + this.acceleration = acceleration; + this.altitude = altitude; + } + + // Create initial jump conditions with adjusted velocity/acceleration and a random initial altitude + private static InitialJumpConditions create(float terminalVelocity, float gravitationalAcceleration) { + final int altitude = (int) (9001.0f * random.nextFloat() + 1000); + return new InitialJumpConditions(terminalVelocity, gravitationalAcceleration, + plusMinus5Percent(terminalVelocity), plusMinus5Percent(gravitationalAcceleration), altitude); + } + + private static float plusMinus5Percent(float value) { + return value + ((value * random.nextFloat()) / 20.0f) - ((value * random.nextFloat()) / 20.0f); + } + + float getOriginalTerminalVelocity() { + return originalTerminalVelocity; + } + + float getOriginalAcceleration() { + return originalAcceleration; + } + + float getTerminalVelocity() { + return terminalVelocity; + } + + float getAcceleration() { + return acceleration; + } + + int getAltitude() { + return altitude; + } + + float getTimeOfTerminalAccelerationReached() { + return terminalVelocity / acceleration; + } + } +} \ No newline at end of file diff --git a/84_Super_Star_Trek/README.md b/84_Super_Star_Trek/README.md index 4a96f528..77fae3d8 100644 --- a/84_Super_Star_Trek/README.md +++ b/84_Super_Star_Trek/README.md @@ -101,4 +101,4 @@ Instructions in this directory at instructions.txt #### External Links - - Super Star Trek in C++ : https://www.codeproject.com/Articles/28399/The-Object-Oriented-Text-Star-Trek-Game-in-C + - C++: https://www.codeproject.com/Articles/28399/The-Object-Oriented-Text-Star-Trek-Game-in-C diff --git a/96_Word/d/.gitignore b/96_Word/d/.gitignore new file mode 100644 index 00000000..d969f6b2 --- /dev/null +++ b/96_Word/d/.gitignore @@ -0,0 +1,2 @@ +*.exe +*.obj diff --git a/96_Word/d/README.md b/96_Word/d/README.md new file mode 100644 index 00000000..764cb141 --- /dev/null +++ b/96_Word/d/README.md @@ -0,0 +1,15 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html) + +Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo). + +The Basic original required words to be exactly five letters in length for the program to behave correctly. +This version does not replicate that limitation, and the test for that requirement is commented out. + +## Running the code + +Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler: +```shell +dmd -dip1000 -run word.d +``` + +[Other compilers](https://dlang.org/download.html) also exist. diff --git a/96_Word/d/word.d b/96_Word/d/word.d new file mode 100644 index 00000000..6f4d46d6 --- /dev/null +++ b/96_Word/d/word.d @@ -0,0 +1,85 @@ +@safe: // Make @safe the default for this file, enforcing memory-safety. +import std; + +void main() +{ + enum width = 80; + writeln(center("Word", width)); + writeln(center("(After Creative Computing Morristown, New Jersey)\n\n\n", width)); + writeln(wrap("I am thinking of a word -- you guess it. I will give you " ~ + "clues to help you get it. Good luck!!\n\n", width)); + + string[] words = ["dinky", "smoke", "water", "grass", "train", "might", "first", + "candy", "champ", "would", "clump", "dopey"]; + + playLoop: while (true) + { + writeln("\n\nYou are starting a new game..."); + + string word = words[uniform(0, $-1)]; // $ is a short-hand for words.length. + int guesses = 0; + string knownLetters = '-'.repeat(word.length).array; + + while (true) + { + writeln("Guess a ", word.length, " letter word"); + string guess = readString.toLower; + if (guess == "?") + { + writeln("The secret word is ", word, "\n"); + continue playLoop; // Start a new game. + } + /* Uncomment this for equivalence with Basic. + if (guess.length != 5) + { + writeln("You must guess a 5 letter word. Start again."); + continue; // Ask for new guess. + } + */ + guesses++; + if (guess == word) + break; // Done guessing + string commonLetters; + foreach (i, wordLetter; word) + foreach (j, guessLetter; guess) + if (guessLetter == wordLetter) + { + commonLetters ~= guessLetter; + if (i == j) + knownLetters.replaceInPlace(i, i + 1, [guessLetter]); + } + writeln("There were ", commonLetters.length, " matches and the common letters were... ", commonLetters); + writeln("From the exact letter matches, you know................ ", knownLetters); + if (knownLetters == word) + break; // Done guessing + if (commonLetters.length < 2) + writeln("If you give up, type '?' for your next guess."); + writeln; + } + + writeln("You have guessed the word. It took ", guesses, " guesses!"); + write("\n\nWant to play again? "); + if (readString.toLower != "yes") + break; // Terminate playLoop + } +} + +/// Read a string from standard input, stripping newline and other enclosing whitespace. +string readString() nothrow +{ + try + return trustedReadln.strip; + catch (Exception) // readln throws on I/O and Unicode errors, which we handle here. + return ""; +} + +/** An @trusted wrapper around readln. + * + * This is the only function that formally requires manual review for memory-safety. + * [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com) + * which would remove the need to have any @trusted code in this program. + */ +string trustedReadln() @trusted +{ + return readln; +} diff --git a/HOW_TO_RUN_THE_GAMES.md b/HOW_TO_RUN_THE_GAMES.md new file mode 100644 index 00000000..44d82e98 --- /dev/null +++ b/HOW_TO_RUN_THE_GAMES.md @@ -0,0 +1,85 @@ +# How to run the games + +The games in this repository have been translated into a number of different languages. How to run them depends on the target language. + +## csharp + +### dotnet command-line + +The best cross-platform method for running the csharp examples is with the `dotnet` command-line tool. This can be downloaded for **MacOS**, **Windows** and **Linux** from [dotnet.microsoft.com](https://dotnet.microsoft.com/). + +From there, the program can be run by + +1. Opening a terminal window +1. Navigating to the corresponding directory +1. Starting with `dotnet run` + +### Visual Studio + +Alternatively, for non-dotnet compatible translations, you will need [Visual Studio](https://visualstudio.microsoft.com/vs/community/) which can be used to both open the project and run the example. + +1. Open the corresponding `.csproj` or `.sln` file +1. Click `Run` from within the Visual Studio IDE + +## java + +The Java translations can be run via the command line or from an IDE such as [Eclipse](https://www.eclipse.org/downloads/packages/release/kepler/sr1/eclipse-ide-java-developers) or [IntelliJ](https://www.jetbrains.com/idea/) + +To run from the command line, you will need a Java SDK (eg. [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) or [Open JDK](https://openjdk.java.net/)). + +1. Navigate to the corresponding directory. +1. Compile the program with `javac`: + * eg. `javac AceyDuceyGame.java` +1. Run the compiled program with `java`: + * eg. `java AceyDuceyGame` + +## javascript + +The javascript examples can be run from within your web browser: + +1. Simply open the corresponding `.html` file from your web browser. + +## pascal + +The pascal examples can be run using [Free Pascal](https://www.freepascal.org/). Additionally, `.lsi` project files can be opened with the [Lazarus Project IDE](https://www.lazarus-ide.org/). + +The pascal examples include both *simple* (single-file) and *object-oriented* (in the `/object-pascal`directories) examples. + +1. You can compile the program from the command line with the `fpc` command. + * eg. `fpc amazing.pas` +1. The output is an executable file that can be run directly. + +## perl + +The perl translations can be run using a perl interpreter (a copy can be downloaded from [perl.org](https://www.perl.org/)) if not already installed. + +1. From the command-line, navigate to the corresponding directory. +1. Invoke with the `perl` command. + * eg. `perl aceyducey.pl` + +## python + +The python translations can be run from the command line by using the `py` interpreter. If not already installed, a copy can be downloaded from [python.org](https://www.python.org/downloads/) for **Windows**, **MacOS** and **Linux**. + +1. From the command-line, navigate to the corresponding directory. +1. Invoke with the `py` or `python` interpreter (depending on your python version). + * eg. `py acey_ducey_oo.py` + * eg. `python aceyducey.py` + +**Note** + +Some translations include multiple versions for python, such as `acey ducey` which features versions for Python 2 (`aceyducey.py`) and Python 3 (`acey_ducey.py`) as well as an extra object-oriented version (`acey_ducey_oo.py`). + +You can manage and use different versions of python with [pip](https://pypi.org/project/pip/). + +## ruby + +If you don't already have a ruby interpreter, you can download it from the [ruby project site](https://www.ruby-lang.org/en/). + +1. From the command-line, navigate to the corresponding directory. +1. Invoke with the `ruby` tool. + * eg. `ruby aceyducey.rb` + +## vbnet + +Follow the same steps as for the [csharp](#csharp) translations. This can be run with `dotnet` or `Visual Studio`.