From d6446929121c7fc05cfc7e2c4e2e168205d648bd Mon Sep 17 00:00:00 2001 From: Alaa Sarhan Date: Sun, 2 Jan 2022 08:13:52 +0100 Subject: [PATCH 01/26] implement the 13th game - Bounce in ruby --- 13_Bounce/ruby/bounce.rb | 181 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 13_Bounce/ruby/bounce.rb diff --git a/13_Bounce/ruby/bounce.rb b/13_Bounce/ruby/bounce.rb new file mode 100644 index 00000000..97c42999 --- /dev/null +++ b/13_Bounce/ruby/bounce.rb @@ -0,0 +1,181 @@ +## Global constants + +# Gravity accelaration (F/S^2) ~= 32 +G = 32 + +# Used to indent the plotting of ball positions +# so that the height digits don't affect +# where we start plotting ball positions +BALL_PLOT_INDENT = "\t" + +# The deviation between current plotted height and the actual +# height of the ball that we will accept to plot the ball in +# that plotted height +BALL_PLOT_DEVIATION = 0.25 + +# The step we will take as we move down vertically while +# plotting ball positions +BALL_PLOT_HEIGHT_STEP = 0.5 + + +## Helper functions + +# Calculates the bounce speed (up) of the ball for a given +# bounce number and coefficient +def calc_velocity_for_bounce(v0, bounce, coefficient) + v = v0 * coefficient**bounce +end + +# Check https://physics.stackexchange.com/a/333436 for nice explanation +def calc_bounce_total_time(v0, bounce, coefficient) + v = calc_velocity_for_bounce(v0, bounce, coefficient) + t = 2 * v / G +end + +# Check https://physics.stackexchange.com/a/333436 for nice explanation +def calc_ball_height(v0, bounce, coefficient, t) + v = calc_velocity_for_bounce(v0, bounce, coefficient) + h = v * t - 0.5 * G * t**2 +end + +def heighest_position_in_next_bounce(time_in_bounce, i) + time_in_next_bounce = time_in_bounce[i+1] + return -1 if time_in_next_bounce.nil? + return calc_ball_height(v0, i, c, time_in_next_bounce / 2) unless time_in_next_bounce.nil? +end + +def intro + puts <<~INSTRUCTIONS + BOUNCE + CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + + THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY + OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF + ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION + COEFFICIENCY (LESS THAN 1). + + YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN + 'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY). + INSTRUCTIONS +end + + +## Plottin functions + +def plot_header + puts + puts "FEET" +end + +def plot_bouncing_ball(strobbing_time, v0, c) + ## Initializing helper values + + # How many bounces we want to plot + # original BASIC version is 70 / (V / (16 * S2)) + # 70 is assumed to be an arbitrary number higher than 2G and 16 is 1/2G + bounces_to_plot = (G**2 / (v0 / strobbing_time)).to_i + + # Holds the total time the ball spends in the air in every bounce + time_in_bounce = bounces_to_plot.times.map { |i| calc_bounce_total_time v0, i, c } + + plot_width = 0 + + # Calculate the highest position for the ball after the very first bounce + plot_y = (calc_ball_height(v0, 0, c, v0/G) + 0.5).to_i + + while plot_y >= 0 do + # We will print only whole-number heights + print plot_y.to_i if plot_y.to_i === plot_y + + print BALL_PLOT_INDENT + + bounces_to_plot.times { |i| + (0..time_in_bounce[i]).step(strobbing_time) { |t| + ball_pos = calc_ball_height v0, i, c, t + + # If the ball is within the acceptable deviation + # from the current height, we will plot it + if (plot_y - ball_pos).abs <= BALL_PLOT_DEVIATION then + print "0" + else + print " " + end + + # Increment the plot width when we are plotting height = 0 + # which will definitely be the longest since it never gets + # skipped by line 98 + plot_width += 1 if plot_y == 0 + } + + if heighest_position_in_next_bounce(time_in_bounce, i) < plot_y then + # If we got no more ball positions at or above current height, we can skip + # the rest of the bounces and move down to the next height to plot + puts + break + end + } + + plot_y -= BALL_PLOT_HEIGHT_STEP + end + + # Return plot_width to be used by the plot_footer + plot_width +end + +def plot_footer (plot_width, strobbing_time) + # Dotted separator line + puts + print BALL_PLOT_INDENT + (plot_width).times { |_| print "." } + puts + + # Time values line + print BALL_PLOT_INDENT + points_in_sec = (1 / strobbing_time).to_i + plot_width.times { |i| + if i % points_in_sec == 0 then + print (i / points_in_sec).to_i + else + print " " + end + } + puts + + # Time unit line + print BALL_PLOT_INDENT + (plot_width / 2 - 4).to_i.times { |_| print " " } + puts "SECONDS" + puts +end + +def game_loop + # Read strobing, velocity and coefficient parameters from user input + puts "TIME INCREMENT (SEC)" + strobbing_time = gets.to_f + + puts "VELOCITY (FPS)" + v0 = gets.to_f + + puts "COEFFICIENT" + c = gets.to_f + + ## Plotting + plot_header + + plot_width = plot_bouncing_ball strobbing_time, v0, c + + plot_footer plot_width, strobbing_time +end + +## Entry point +begin + intro + while true + game_loop + end +rescue SystemExit, Interrupt + exit +rescue => exception + p exception +end \ No newline at end of file From 3f42a1b4d63f18cb927a3a08ffce8b5900171058 Mon Sep 17 00:00:00 2001 From: Alaa Sarhan Date: Mon, 3 Jan 2022 02:06:08 +0100 Subject: [PATCH 02/26] add newline eof --- 13_Bounce/ruby/bounce.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/13_Bounce/ruby/bounce.rb b/13_Bounce/ruby/bounce.rb index 97c42999..9740509d 100644 --- a/13_Bounce/ruby/bounce.rb +++ b/13_Bounce/ruby/bounce.rb @@ -178,4 +178,4 @@ rescue SystemExit, Interrupt exit rescue => exception p exception -end \ No newline at end of file +end From 807d6e5bf53f5b216f8d24874f08a40e6731a3d3 Mon Sep 17 00:00:00 2001 From: Alaa Sarhan Date: Mon, 3 Jan 2022 02:15:59 +0100 Subject: [PATCH 03/26] fix missing parameters --- 13_Bounce/ruby/bounce.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/13_Bounce/ruby/bounce.rb b/13_Bounce/ruby/bounce.rb index 9740509d..339c7851 100644 --- a/13_Bounce/ruby/bounce.rb +++ b/13_Bounce/ruby/bounce.rb @@ -38,7 +38,7 @@ def calc_ball_height(v0, bounce, coefficient, t) h = v * t - 0.5 * G * t**2 end -def heighest_position_in_next_bounce(time_in_bounce, i) +def heighest_position_in_next_bounce(time_in_bounce, v0, i, c) time_in_next_bounce = time_in_bounce[i+1] return -1 if time_in_next_bounce.nil? return calc_ball_height(v0, i, c, time_in_next_bounce / 2) unless time_in_next_bounce.nil? @@ -82,11 +82,12 @@ def plot_bouncing_ball(strobbing_time, v0, c) plot_width = 0 # Calculate the highest position for the ball after the very first bounce - plot_y = (calc_ball_height(v0, 0, c, v0/G) + 0.5).to_i + plotted_height = (calc_ball_height(v0, 0, c, v0/G) + 0.5).to_i - while plot_y >= 0 do + ## Plotting bouncing ball + while plotted_height >= 0 do # We will print only whole-number heights - print plot_y.to_i if plot_y.to_i === plot_y + print plotted_height.to_i if plotted_height.to_i === plotted_height print BALL_PLOT_INDENT @@ -96,7 +97,7 @@ def plot_bouncing_ball(strobbing_time, v0, c) # If the ball is within the acceptable deviation # from the current height, we will plot it - if (plot_y - ball_pos).abs <= BALL_PLOT_DEVIATION then + if (plotted_height - ball_pos).abs <= BALL_PLOT_DEVIATION then print "0" else print " " @@ -105,10 +106,10 @@ def plot_bouncing_ball(strobbing_time, v0, c) # Increment the plot width when we are plotting height = 0 # which will definitely be the longest since it never gets # skipped by line 98 - plot_width += 1 if plot_y == 0 + plot_width += 1 if plotted_height == 0 } - if heighest_position_in_next_bounce(time_in_bounce, i) < plot_y then + if heighest_position_in_next_bounce(time_in_bounce, v0, i, c) < plotted_height then # If we got no more ball positions at or above current height, we can skip # the rest of the bounces and move down to the next height to plot puts @@ -116,7 +117,7 @@ def plot_bouncing_ball(strobbing_time, v0, c) end } - plot_y -= BALL_PLOT_HEIGHT_STEP + plotted_height -= BALL_PLOT_HEIGHT_STEP end # Return plot_width to be used by the plot_footer @@ -160,7 +161,7 @@ def game_loop puts "COEFFICIENT" c = gets.to_f - ## Plotting + # Plotting plot_header plot_width = plot_bouncing_ball strobbing_time, v0, c @@ -168,7 +169,9 @@ def game_loop plot_footer plot_width, strobbing_time end -## Entry point + +## Game entry point + begin intro while true From d77d3ee168913e7cce342c2e3bd9c778c546eb1b Mon Sep 17 00:00:00 2001 From: Tom Wyant Date: Mon, 3 Jan 2022 15:14:45 -0500 Subject: [PATCH 04/26] Port 03_Animal to Perl. Replaed array-based simulation of binary tree with hash-based simulation of binary tree. --- 03_Animal/perl/animal.pl | 223 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100755 03_Animal/perl/animal.pl diff --git a/03_Animal/perl/animal.pl b/03_Animal/perl/animal.pl new file mode 100755 index 00000000..a7c8bf9e --- /dev/null +++ b/03_Animal/perl/animal.pl @@ -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, 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 + +=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, and/or the +Gnu GPL at L. + +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 : From ec189883f96a93d15355dc0c34c47fa9719859e9 Mon Sep 17 00:00:00 2001 From: RibTips <36372030+ribtips@users.noreply.github.com> Date: Mon, 3 Jan 2022 15:59:14 -0500 Subject: [PATCH 05/26] Perl version of Chomp --- 26_Chomp/perl/chomp.pl | 173 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 26_Chomp/perl/chomp.pl diff --git a/26_Chomp/perl/chomp.pl b/26_Chomp/perl/chomp.pl new file mode 100644 index 00000000..34727626 --- /dev/null +++ b/26_Chomp/perl/chomp.pl @@ -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=; + 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=; + 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=; + chomp($players); + } + until ($rows > 0 && $rows < 10) { + print "HOW MANY ROWS\n"; + $rows=; + chomp($rows); + if ($rows > 9) { + print "TOO MANY ROWS (9 IS MAXIMUM). NOW, "; + } + } + until ($cols > 0 && $cols < 10) { + print "HOW MANY COLUMNS\n"; + $cols=; + 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 = ; + 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"; + } +} From d10e80e2c0e0eefca5cd3ecfa368994d5a7f9271 Mon Sep 17 00:00:00 2001 From: Tom Wyant Date: Mon, 3 Jan 2022 17:12:34 -0500 Subject: [PATCH 06/26] Ported 73_Reverse to Perl. In a language with list assignments, array slices, and a reverse() built-in, the reversal can be done in one statement. --- 73_Reverse/perl/reverse.pl | 234 +++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100755 73_Reverse/perl/reverse.pl diff --git a/73_Reverse/perl/reverse.pl b/73_Reverse/perl/reverse.pl new file mode 100755 index 00000000..94ef40e6 --- /dev/null +++ b/73_Reverse/perl/reverse.pl @@ -0,0 +1,234 @@ +#!/usr/bin/env perl + +use 5.010; # To get 'state' and 'say' + +use strict; # Require explicit declaration of variables +use warnings; # Enable optional compiler warnings + +use English; # Use more friendly names for Perl's magic variables +use List::Util qw{ shuffle }; # Shuffle an array. +use Term::ReadLine; # Prompt and return user input + +our $VERSION = '0.000_01'; + +# Manifest constant for size of list. +use constant NUMBER_OF_NUMBERS => 9; + +print <<'EOD'; + REVERSE + Creative Computing Morristown, New Jersey + + + +Reverse -- a game of skill + +EOD + +# Display the rules if desired. There is no straightforward way to +# interpolate a manifest constant into a string, but @{[ ... ]} will +# interpolate any expression. +print <<"EOD" if get_yes_no( 'Do you want the rules' ); + +This is the game of 'Reverse'. To win, all you have +to do is arrange a list of numbers (1 through @{[ NUMBER_OF_NUMBERS ]}) +in numerical order from left to right. To move, you +tell me how many numbers (counting from the left) to +reverse. For example, if the current list is: + +2 3 4 5 1 6 7 8 9 + +and you reverse 4, the result will be: + +5 4 3 2 1 6 7 8 9 + +Now if you reverse 5, you win! + +1 2 3 4 5 6 7 8 9 + +No doubt you will like this game, but +if you want to quit, reverse 0 (zero). + +EOD + +while ( 1 ) { # Iterate until something interrupts us. + + # Populate the list with the integers from 1, shuffled. If we + # accidentally generate a winning list, just redo the loop. + my @list = shuffle( 1 .. NUMBER_OF_NUMBERS ); + redo if is_win( \@list ); + + print <<"EOD"; + +Here we go ... The list is: +EOD + + my $moves = 0; # Move counter + + while ( 1 ) { # Iterate until something interrupts us. + print <<"EOD"; + +@list + +EOD + + # Read the number of values to reverse. Zero is special-cased to + # take us out of this loop. + last unless my $max_index = get_input( + 'How many shall I reverse (0 to quit)? ', + sub { + return m/ \A [0-9]+ \z /smx && + $ARG <= NUMBER_OF_NUMBERS; + }, + "Oops! Too many! I can reverse at most " . + NUMBER_OF_NUMBERS, + ); + + --$max_index; # Convert number to reverse to upper index + + # Use a Perl array slice and the reverse() built-in to reverse + # the beginning of the list. + @list[ 0 .. $max_index ] = reverse @list[ 0 .. $max_index ]; + + $moves++; # Count a move + + # If we have not won, iterate again. + next unless is_win( \@list ); + + # Announce the win, and drop out of the loop. + print <<"EOD"; + +You won it in $moves moves!!! +EOD + last; + } + + # Drop out of this loop unless the player wants to play again. + say ''; + last unless get_yes_no( 'Try again' ); +} + +print <<'EOD'; + +O.K. Hope you had fun!! +EOD + +# Get input from the user. The arguments are: +# * The prompt +# * A reference to validation code. This code receives the response in +# $ARG and returns true for a valid response. +# * A warning to print if the response is not valid. This must end in a +# return. +# The first valid response is returned. An end-of-file terminates the +# script. +sub get_input { + my ( $prompt, $validate, $warning ) = @ARG; + + # If no validator is passed, default to one that always returns + # true. + $validate ||= sub { 1 }; + + # Create the readline object. The 'state' causes the variable to be + # initialized only once, no matter how many times this subroutine is + # called. The do { ... } is a compound statement used because we + # need to tweak the created object before we store it. + state $term = do { + my $obj = Term::ReadLine->new( 'reverse' ); + $obj->ornaments( 0 ); + $obj; + }; + + while ( 1 ) { # Iterate indefinitely + + # Read the input into the topic variable, localized to prevent + # Spooky Action at a Distance. We exit on undef, which signals + # end-of-file. + exit unless defined( local $ARG = $term->readline( $prompt ) ); + + # Return the input if it is valid. + return $ARG if $validate->(); + + # Issue the warning, and go around the merry-go-round again. + warn $warning; + } +} + +# Get a yes-or-no answer. The argument is the prompt, which will have +# '? [y/n]: ' appended. The donkey work is done by get_input(), which is +# requested to validate the response as beginning with 'y' or 'n', +# case-insensitive. The return is a true value for 'y' and a false value +# for 'n'. +sub get_yes_no { + my ( $prompt ) = @ARG; + state $map_answer = { + n => 0, + y => 1, + }; + my $resp = lc get_input( + "$prompt? [y/n]: ", + sub { m/ \A [yn] /smxi }, + "Please respond 'y' or 'n'\n", + ); + return $map_answer->{ substr $resp, 0, 1 }; +} + +# Determine if a given list represents a win. The argument is a +# reference to the array containing the list. We return a true value for +# a win, or a false value otherwise. +sub is_win { + my ( $list ) = @_; + my $expect = 1; # We expect the first element to be 1; + + # Iterate over the array. + foreach my $element ( @{ $list } ) { + + # If the element does not have the expected value, we return + # false. We post-increment the expected value en passant. + $element == $expect++ + or return 0; + } + + # All elements had the expected value, so we won. Return a true + # value. + return 1; +} + +__END__ + +=head1 TITLE + +reverse.pl - Play the game 'reverse' from Basic Computer Games + +=head1 SYNOPSIS + + reverse.pl + +=head1 DETAILS + +This Perl script is a port of C, which is the 73rd entry in +Basic Computer Games. + +The cool thing about this port is the fact that, in a language with +array slices, list assignments, and a C built-in, the +reversal is a single assignment statement. + +=head1 PORTED BY + +Thomas R. Wyant, III F + +=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, and/or the +Gnu GPL at L. + +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 : From d32c56800a459936e1d8703c7bd3dbb38cfc2379 Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Tue, 4 Jan 2022 10:13:48 +1100 Subject: [PATCH 07/26] initial kotlin implementation of "King" --- 53_King/king_variable_update.bas | 306 ++++++++++++++++ 53_King/kotlin/King.kt | 586 +++++++++++++++++++++++++++++++ 2 files changed, 892 insertions(+) create mode 100644 53_King/king_variable_update.bas create mode 100644 53_King/kotlin/King.kt diff --git a/53_King/king_variable_update.bas b/53_King/king_variable_update.bas new file mode 100644 index 00000000..c88edf93 --- /dev/null +++ b/53_King/king_variable_update.bas @@ -0,0 +1,306 @@ +1 PRINT TAB(34);"KING" +2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +3 PRINT:PRINT:PRINT +4 PRINT "DO YOU WANT INSTRUCTIONS"; +5 INPUT Z$ +6 YEARS_REQUIRED=8 +10 IF LEFT$(Z$,1)="N" THEN 47 + 11 IF Z$="AGAIN" THEN 1960 + 12 PRINT:PRINT:PRINT + 20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS" + 22 PRINT "DETINU, RALLODS SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR" + 24 PRINT "JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE" + 26 PRINT "MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY." + 28 PRINT "THE MONEY SYSTEM IS RALLODS, AND EACH PERSON NEEDS 100" + 30 PRINT "RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES" + 32 PRINT "FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT" + 34 PRINT "FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND" + 36 PRINT "WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD" + 38 PRINT "TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT" + 40 PRINT "THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER" + 42 PRINT "SQUARE MILE TO PLANT." + 44 PRINT "YOUR GOAL IS TO COMPLETE YOUR";YEARS_REQUIRED;"YEAR TERM OF OFFICE." + 46 PRINT "GOOD LUCK!" + +47 PRINT + +50 RALLODS=INT(60000+(1000*RND(1))-(1000*RND(1))) +55 COUNTRYMEN=INT(500+(10*RND(1))-(10*RND(1))) +65 LANDAREA=2000 +100 LANDPRICE=INT(10*RND(1)+95) +102 PRINT +105 PRINT "YOU NOW HAVE ";RALLODS;" RALLODS IN THE TREASURY." +110 PRINT INT(COUNTRYMEN);:PRINT "COUNTRYMEN, "; +115 COST_TO_PLANT=INT(((RND(1)/2)*10+10)) +120 IF FOREIGN_WORKERS=0 THEN 140 +130 PRINT INT(FOREIGN_WORKERS);"FOREIGN WORKERS, "; +140 PRINT "AND";INT(LANDAREA);"SQ. MILES OF LAND." +150 PRINT "THIS YEAR INDUSTRY WILL BUY LAND FOR";LANDPRICE; +152 PRINT "RALLODS PER SQUARE MILE." +155 PRINT "LAND CURRENTLY COSTS";COST_TO_PLANT;"RALLODS PER SQUARE MILE TO PLANT." +162 PRINT + +200 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY"; +210 INPUT SELL_TO_INDUSTRY +215 IF SELL_TO_INDUSTRY<0 THEN 200 +220 IF SELL_TO_INDUSTRY<=LANDAREA-1000 THEN 300 +230 PRINT "*** THINK AGAIN. YOU ONLY HAVE";LANDAREA-1000;"SQUARE MILES OF FARM LAND." + +240 IF EXPLANATION_GIVEN THEN 200 + 250 PRINT:PRINT "(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE" + 260 PRINT "FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES," + 270 PRINT "THICKER TOP SOIL, ETC.)" + 280 EXPLANATION_GIVEN=TRUE +299 GOTO 200 + +300 LANDAREA=INT(LANDAREA-SELL_TO_INDUSTRY) +310 RALLODS=INT(RALLODS+(SELL_TO_INDUSTRY*LANDPRICE)) +320 PRINT "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN"; +340 INPUT WELFARE +342 IF WELFARE<0 THEN 320 +350 IF WELFARE0 THEN 1002 +602 IF WELFARE<>0 THEN 1002 +604 IF PLANTING_AREA<>0 THEN 1002 +606 IF MONEY_SPENT_ON_POLLUTION_CONTROL<>0 THEN 1002 + +609 PRINT +612 PRINT "GOODBYE." +614 PRINT "(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER" +616 PRINT "'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START" +617 PRINT "OF THE GAME)." +618 STOP + +1000 GOTO 600 + +1002 PRINT +1003 PRINT + +1010 RALLODS=INT(RALLODS-MONEY_SPENT_ON_POLLUTION_CONTROL) +1020 ORIGINAL_RALLODS=RALLODS + +1100 IF INT(WELFARE/100-COUNTRYMEN)>=0 THEN 1120 +1105 IF WELFARE/100<50 THEN 1700 +1110 PRINT INT(COUNTRYMEN-(WELFARE/100));"COUNTRYMEN DIED OF STARVATION" + +1120 POLLUTION_DEATHS=INT(RND(1)*(2000-LANDAREA)) +1122 IF MONEY_SPENT_ON_POLLUTION_CONTROL<25 THEN 1130 +1125 POLLUTION_DEATHS=INT(POLLUTION_DEATHS/(MONEY_SPENT_ON_POLLUTION_CONTROL/25)) + +1130 IF POLLUTION_DEATHS<=0 THEN 1150 +1140 PRINT POLLUTION_DEATHS;"COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION" + +1150 IF INT((WELFARE/100)-COUNTRYMEN)<0 THEN 1170 +1160 IF POLLUTION_DEATHS>0 THEN 1180 +1165 GOTO 1200 + +1170 PRINT " YOU WERE FORCED TO SPEND";INT((POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100)))*9); +1172 PRINT "RALLODS ON FUNERAL EXPENSES" +1174 DEATHS=INT(POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100))) +1175 RALLODS=INT(RALLODS-((POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100)))*9)) +1176 GOTO 1185 + +1180 PRINT " YOU WERE FORCED TO SPEND ";INT(POLLUTION_DEATHS*9);"RALLODS ON "; +1181 PRINT "FUNERAL EXPENSES." +1182 DEATHS=POLLUTION_DEATHS +1183 RALLODS=INT(RALLODS-(POLLUTION_DEATHS*9)) + +1185 IF RALLODS>=0 THEN 1194 +1187 PRINT " INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD" +1189 LANDAREA=INT(LANDAREA+(RALLODS/LANDPRICE)) +1190 RALLODS=0 + +1194 COUNTRYMEN=INT(COUNTRYMEN-DEATHS) + +1200 IF SELL_TO_INDUSTRY=0 THEN 1250 +1220 NEW_FOREIGNERS=INT(SELL_TO_INDUSTRY+(RND(1)*10)-(RND(1)*20)) +1224 IF FOREIGN_WORKERS>0 THEN 1230 +1226 NEW_FOREIGNERS=NEW_FOREIGNERS+20 + +1230 PRINT NEW_FOREIGNERS;"WORKERS CAME TO THE COUNTRY AND"; + +1250 IMMIGRATION=INT(((WELFARE/100-COUNTRYMEN)/10)+(MONEY_SPENT_ON_POLLUTION_CONTROL/25)-((2000-LANDAREA)/50)-(POLLUTION_DEATHS/2)) +1255 PRINT ABS(IMMIGRATION);"COUNTRYMEN "; +1260 IF IMMIGRATION<0 THEN 1275 +1265 PRINT "CAME TO"; +1270 GOTO 1280 +1275 PRINT "LEFT"; +1280 PRINT " THE ISLAND." +1290 COUNTRYMEN=INT(COUNTRYMEN+IMMIGRATION) + + +1292 FOREIGN_WORKERS=INT(FOREIGN_WORKERS+NEW_FOREIGNERS) + +1305 CROP_LOSS=INT(((2000-LANDAREA)*((RND(1)+1.5)/2))) +1310 IF FOREIGN_WORKERS=0 THEN 1324 +1320 PRINT "OF ";INT(PLANTING_AREA);"SQ. MILES PLANTED,"; +1324 IF PLANTING_AREA>CROP_LOSS THEN 1330 +1326 CROP_LOSS=PLANTING_AREA +1330 PRINT " YOU HARVESTED ";INT(PLANTING_AREA-CROP_LOSS);"SQ. MILES OF CROPS." +1340 IF CROP_LOSS=0 THEN 1370 +1344 IF T1>=2 THEN 1370 +1350 PRINT " (DUE TO "; +1355 IF T1=0 THEN 1365 +1360 PRINT "INCREASED "; +1365 PRINT "AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)" +1370 AGRICULTURAL_INCOME=INT((PLANTING_AREA-CROP_LOSS)*(LANDPRICE/2)) +1380 PRINT "MAKING";INT(AGRICULTURAL_INCOME);"RALLODS." +1390 RALLODS=INT(RALLODS+AGRICULTURAL_INCOME) + +REM I think tourism calculations are actually wrong in the original code! + +1400 V1=INT(((COUNTRYMEN-IMMIGRATION)*22)+(RND(1)*500)) +1405 V2=INT((2000-LANDAREA)*15) +1410 PRINT " YOU MADE";ABS(INT(V1-V2));"RALLODS FROM TOURIST TRADE." +1420 IF V2=0 THEN 1450 +1425 IF V1-V2>=V3 THEN 1450 +1430 PRINT " DECREASE BECAUSE "; +1435 G1=10*RND(1) +1440 IF G1<=2 THEN 1460 +1442 IF G1<=4 THEN 1465 +1444 IF G1<=6 THEN 1470 +1446 IF G1<=8 THEN 1475 +1448 IF G1<=10 THEN 1480 +1450 V3=INT(RALLODS+V3) +1451 RALLODS=INT(RALLODS+V3) +1452 GOTO 1500 +1460 PRINT "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." +1462 GOTO 1450 +1465 PRINT "AIR POLLUTION IS KILLING GAME BIRD POPULATION." +1467 GOTO 1450 +1470 PRINT "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." +1472 GOTO 1450 +1475 PRINT "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." +1477 GOTO 1450 +1480 PRINT "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." +1482 GOTO 1450 +1500 IF DEATHS>200 THEN 1600 +1505 IF COUNTRYMEN<343 THEN 1700 +1510 IF (ORIGINAL_RALLODS/100)>5 THEN 1800 +1515 IF FOREIGN_WORKERS>COUNTRYMEN THEN 1550 +1520 IF YEARS_REQUIRED-1=X5 THEN 1900 +1545 GOTO 2000 +1550 PRINT +1552 PRINT +1560 PRINT "THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER" +1562 PRINT "OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND" +1564 PRINT "TAKEN OVER THE COUNTRY." +1570 IF RND(1)<=.5 THEN 1580 +1574 PRINT "YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW" +1576 PRINT "RESIDING IN PRISON." +1578 GOTO 1590 +1580 PRINT "YOU HAVE BEEN ASSASSINATED." +1590 PRINT +1592 PRINT +1596 STOP +1600 PRINT +1602 PRINT +1610 PRINT DEATHS;"COUNTRYMEN DIED IN ONE YEAR!!!!!" +1615 PRINT "DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY" +1620 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU" +1622 M6=INT(RND(1)*10) +1625 IF M6<=3 THEN 1670 +1630 IF M6<=6 THEN 1680 +1635 IF M6<=10 THEN 1690 +1670 PRINT "ALSO HAD YOUR LEFT EYE GOUGED OUT!" +1672 GOTO 1590 +1680 PRINT "HAVE ALSO GAINED A VERY BAD REPUTATION." +1682 GOTO 1590 +1690 PRINT "HAVE ALSO BEEN DECLARED NATIONAL FINK." +1692 GOTO 1590 + +1700 PRINT +1702 PRINT +1710 PRINT "OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU" +1715 PRINT "WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)" +1720 PRINT "HATE YOUR GUTS." +1730 GOTO 1570 +1800 IF DEATHS-POLLUTION_DEATHS<2 THEN 1515 +1807 PRINT +1815 PRINT "MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID" +1820 PRINT "NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED" +1825 PRINT "OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE" +1830 PRINT "BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE." +1835 PRINT "THE CHOICE IS YOURS." +1840 PRINT "IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER" +1845 PRINT "BEFORE PROCEEDING." +1850 GOTO 1590 +1900 PRINT +1902 PRINT +1920 PRINT "CONGRATULATIONS!!!!!!!!!!!!!!!!!!" +1925 PRINT "YOU HAVE SUCCESFULLY COMPLETED YOUR";YEARS_REQUIRED;"YEAR TERM" +1930 PRINT "OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT" +1935 PRINT "NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD" +1940 PRINT "LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT" +1945 PRINT "PLAYS THIS GAME." +1950 GOTO 1590 + +1960 PRINT "HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED"; +1961 INPUT X5 +1962 IF X5<0 THEN 1590 +1963 IF X5<8 THEN 1969 +1965 PRINT " COME ON, YOUR TERM IN OFFICE IS ONLY";YEARS_REQUIRED;"YEARS." +1967 GOTO 1960 +1969 PRINT "HOW MUCH DID YOU HAVE IN THE TREASURY"; +1970 INPUT RALLODS +1971 IF RALLODS<0 THEN 1590 +1975 PRINT "HOW MANY COUNTRYMEN"; +1976 INPUT COUNTRYMEN +1977 IF COUNTRYMEN<0 THEN 1590 +1980 PRINT "HOW MANY WORKERS"; +1981 INPUT FOREIGN_WORKERS +1982 IF FOREIGN_WORKERS<0 THEN 1590 +1990 PRINT "HOW MANY SQUARE MILES OF LAND"; +1991 INPUT LANDAREA +1992 IF LANDAREA<0 THEN 1590 +1993 IF LANDAREA>2000 THEN 1996 +1994 IF LANDAREA>1000 THEN 100 +1996 PRINT " COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND" +1997 PRINT " AND 10,000 SQ. MILES OF FOREST LAND." +1998 GOTO 1990 + +2000 X5=X5+1 +2020 DEATHS=0 +2040 GOTO 100 +2046 END diff --git a/53_King/kotlin/King.kt b/53_King/kotlin/King.kt new file mode 100644 index 00000000..2200a9be --- /dev/null +++ b/53_King/kotlin/King.kt @@ -0,0 +1,586 @@ +import kotlin.math.abs +import kotlin.random.Random +import kotlin.system.exitProcess + +lateinit var gameState: GameState +const val INCLUDE_BUGS_FROM_ORIGINAL = false + +val rnd: Double get() = Random.nextDouble() +fun tab(i: Int) = " ".repeat(i) +class EndOfInputException : Throwable() + +fun main() { + header() + + print("DO YOU WANT INSTRUCTIONS? ") + readLine()?.apply { + gameState = if (startsWith("AGAIN")) loadOldGame() else GameState() + if (startsWith("Y")) instructions(gameState.yearsRequired) + } + ?: throw EndOfInputException() + + try { + with(gameState) { + while(currentYear < yearsRequired) { + recalculateLandCost() + displayStatus() + inputLandSale() + performLandSale() + inputWelfare() + performWelfare() + inputPlantingArea() + performPlanting() + inputPollutionControl() + if (zeroInput()) { + displayExitMessage() + exitProcess(0) + } + simulateOneYear() + currentYear ++ + } + } + win(gameState.yearsRequired) + } catch (e: GameEndingException) { + e.displayConsequences() + } +} + +private fun header() { + println("${tab(34)}KING") + println("${tab(14)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + println() + println() + println() +} + +fun instructions(yearsRequired: Int) { + println(""" + + + CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS + DETINU, A SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR + JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE + MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY. + THE MONEY SYSTEM IS RALLODS, AND EACH PERSON NEEDS 100 + RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES + FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT + FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND + WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD + TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT + THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER + SQUARE MILE TO PLANT. + YOUR GOAL IS TO COMPLETE YOUR $yearsRequired YEAR TERM OF OFFICE. + GOOD LUCK! + """.trimIndent() + ) +} + +fun loadOldGame(): GameState = GameState().apply { + + do { + var retry = false + print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED? ") + currentYear = numberInput() + + if (currentYear <= 0) + throw GameEndingException.DataEntryValidation() + + if (currentYear >= yearsRequired) { + println(" COME ON, YOUR TERM IN OFFICE IS ONLY $yearsRequired YEARS.") + retry = true + } + } while (retry) + + print("HOW MUCH DID YOU HAVE IN THE TREASURY? ") + rallods = numberInput() + if (rallods < 0) + throw GameEndingException.DataEntryValidation() + + print("HOW MANY WORKERS? ") + foreignWorkers = numberInput() + if (foreignWorkers < 0) + throw GameEndingException.DataEntryValidation() + + do { + var retry = false + print("HOW MANY SQUARE MILES OF LAND? ") + landArea = numberInput() + if (landArea<0) + throw GameEndingException.DataEntryValidation() + if (landArea > 2000 || landArea <= 1000) { + println(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND") + println(" AND 10,000 SQ. MILES OF FOREST LAND.") + retry = true + } + } while (retry) + +} + + +/** + * All exceptions which indicate the premature ending of the game, due + * to mismanagement, starvation, revolution, or mis-entry of a game state. + */ +sealed class GameEndingException : Throwable() { + abstract fun displayConsequences() + + fun finalFate() { + if (rnd < .5) { + println("YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW") + println("RESIDING IN PRISON.") + } else { + println("YOU HAVE BEEN ASSASSINATED.") + } + println() + println() + } + + class ExtremeMismanagement(private val death: Int) : GameEndingException() { + override fun displayConsequences() { + println() + println("$death COUNTRYMEN DIED IN ONE YEAR!!!!!") + println("DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY") + println("BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU") + println( + when ((rnd * 10.0).toInt()) { + in 0..3 -> "ALSO HAD YOUR LEFT EYE GOUGED OUT!" + in 4..6 -> "HAVE ALSO GAINED A VERY BAD REPUTATION." + else -> "HAVE ALSO BEEN DECLARED NATIONAL FINK." + } + ) + } + } + + class TooManyPeopleDead : GameEndingException() { + // The mistyping of "population" is in the original game. + override fun displayConsequences() { + println(""" + + + OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU + WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING) + HATE YOUR GUTS. + """.trimIndent()) + finalFate() + } + } + + class AntiImmigrationRevolution : GameEndingException() { + override fun displayConsequences() { + println(""" + THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER + OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND + TAKEN OVER THE COUNTRY. + """.trimIndent()) + finalFate() + } + } + + class StarvationWithFullTreasury : GameEndingException() { + override fun displayConsequences() { + println(""" + MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID + NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED + OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE + BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE. + THE CHOICE IS YOURS. + IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER + BEFORE PROCEEDING. + """.trimIndent()) + } + } + + class DataEntryValidation : GameEndingException() { + override fun displayConsequences() { + // no action + } + } + + +} + +fun win(yearsRequired: Int) { + // The misspelling of "successfully" is in the original code. + println(""" + + CONGRATULATIONS!!!!!!!!!!!!!!!!!! + YOU HAVE SUCCESFULLY COMPLETED YOUR $yearsRequired YEAR TERM + OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT + NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD + LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT + PLAYS THIS GAME. + + + """.trimIndent()) +} + +/** + * Record data, allow data input, and process the simulation for the game. + */ +class GameState(val yearsRequired: Int = 8) { + + /** + * The current year. Years start with zero, but we never + * output the current year. + */ + var currentYear = 0 + + /** + * Number of countrymen who have died of either pollution + * or starvation this year. + * It costs 9 rallods to bury a body. + * If you lose 200 people in one year, you will throw an {@see ExtremeMismanagementException} + */ + private var death = 0 + + /** + * Last year's tourist numbers. Use this to check whether the number + * of tourists has gone up or down each year. + */ + private var tourists = 0 + + private var moneySpentOnPollutionControl = 0 + private var moneySpentOnPlanting = 0 + + /** + * Current stock of rallods. + * Player starts with between 59000 and 61000 rallods, but + * mostly distributed close to 60000. 75% of the time it's + * between 59500 and 60500. + */ + var rallods = (60000.0 + (1000.0 * rnd) - (1000.0 * rnd)).toInt() + + /** + * Population. + * Initial population is about to 500. + * 75% of the time it's between 495 and 505. + */ + private var countrymen = (500 + (10 * rnd) - (10 * rnd)).toInt() + + /** + * Land sale price is evenly between 95 and 104 rallods per + * square mile. + * Price doesn't change over the course of the game. + */ + private var landPrice = (10 * rnd + 95).toInt() + + private var plantingArea = 0 + private var welfareThisYear = 0 + + /** + * Land area in square miles. Arable land is 1000 square miles less. + * Almost all calculations use landArea-1000 because only arable + * land is of any use. + */ + var landArea = 2000 + + /** + * Number of foreigners brought in by companies to whom you + * have sold land. If this gets higher than your population, there will + * be a revolution. + */ + var foreignWorkers = 0 + + /** + * Planting cost is recalculated every year. + */ + private var costToPlant: Int = 1 + + /** + * There is a brief explanation of land selling only + * on the first turn. + */ + private var explanationOfSellingGiven = false + + private var sellThisYear: Int = 0 + + /** + * Planting cost is recalculated every year + * at between 10 and 14 rallods. + */ + fun recalculateLandCost() { + costToPlant = ((rnd / 2.0) * 10.0 + 10.0).toInt() + } + + /** + * Show the current status of the world. + */ + fun displayStatus() { + println() + println("YOU NOW HAVE $rallods RALLODS IN THE TREASURY.") + print("$countrymen COUNTRYMEN, ") + if (foreignWorkers != 0) { + println("$foreignWorkers FOREIGN WORKERS, ") + } + println("AND $landArea SQ. MILES OF LAND.") + println("THIS YEAR INDUSTRY WILL BUY LAND FOR $landPrice") + println("RALLODS PER SQUARE MILE.") + println("LAND CURRENTLY COSTS $costToPlant RALLODS PER SQUARE MILE TO PLANT.") + } + + fun displayExitMessage() { + println() + println("GOODBYE.") + println("(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER") + println("'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START") + println("OF THE GAME).") + } + + fun performLandSale() { + landArea -= sellThisYear + rallods += sellThisYear * landPrice + } + + fun performPlanting() { + rallods -= moneySpentOnPlanting + } + + fun performWelfare() { + rallods -= welfareThisYear + } + + + /** + * Ask how much land we want to sell. Immediately get the money. + * The player has to do the calculations to work out how much + * money that makes. + */ + fun inputLandSale() { + do { + print("HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY? ") + sellThisYear = numberInput() + if (sellThisYear > landArea - 1000) { + println("*** THINK AGAIN. YOU ONLY HAVE ${landArea - 1000} SQUARE MILES OF FARM LAND.") + if (!explanationOfSellingGiven) { + println() + println("(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE") + println("FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES,") + println("THICKER TOP SOIL, ETC.)") + explanationOfSellingGiven = true + } + } + } while (sellThisYear <= 0 || sellThisYear > landArea - 1000) + } + + /** + * Input the value of `welfareThisYear` + */ + fun inputWelfare() { + do { + var retry = false + print("HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN? ") + welfareThisYear = numberInput() + + if (welfareThisYear > rallods) { + println(" THINK AGAIN. YOU'VE ONLY $rallods RALLODS IN THE TREASURY") + retry = true + } + + if (welfareThisYear <= 0) { + retry = true + } + } while (retry) + } + + /** + * Get the number of square miles to plant this year. + * Validate the response: + * Each countryman can only plant 2 square miles. + * You can only plant on arable land. + * You may not spend more on planting than your treasury. + */ + fun inputPlantingArea() { + if (welfareThisYear == rallods) { + plantingArea = 0 + } else { + do { + var retry = false + print("HOW MANY SQUARE MILES DO YOU WISH TO PLANT? ") + plantingArea = numberInput() + val moneySpentOnPlanting = plantingArea * costToPlant + + if (plantingArea < 0) { + retry = true + } else if (plantingArea >= 0 && plantingArea > countrymen * 2) { + println(" SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES.") + retry = true + } else if (plantingArea > landArea - 1000) { + println(" SORRY, BUT YOU'VE ONLY ${landArea - 1000} SQ. MILES OF FARM LAND.") + retry = true + } else if (moneySpentOnPlanting > rallods) { + println(" THINK AGAIN. YOU'VE ONLY $rallods RALLODS LEFT IN THE TREASURY.") + retry = true + } + } while (retry) + } + + } + + /** + * Enter amount for pollution control. + * Validate that this does not exceed treasury. + */ + fun inputPollutionControl() { + do { + var retry = false + print("HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL? ") + moneySpentOnPollutionControl = numberInput() + + if (rallods < 0) { + retry = true + } else if (moneySpentOnPollutionControl > rallods) { + println(" THINK AGAIN. YOU ONLY HAVE $rallods RALLODS REMAINING.") + retry = true + } + + } while (retry) + } + + /** + * @return true if all data entered so far has been zero. + */ + fun zeroInput() = sellThisYear == 0 && + welfareThisYear == 0 && + plantingArea == 0 && + moneySpentOnPollutionControl == 0 + + fun simulateOneYear() { + rallods -= moneySpentOnPollutionControl + val rallodsAfterPollutionControl = rallods + + var starvationDeaths = 0 + if (welfareThisYear / 100.0 - countrymen < 0) { + + /* + Wait, WHAT? + If you spend less than 5000 rallods on welfare, no matter the current size of the + population, then you will end the game, with the game claiming that too many + people have died, without showing exactly how many have died? + + https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1105%20IF%20I/100%3C50%20THEN%201700 + */ + if (welfareThisYear / 100.0 < 50) + throw GameEndingException.TooManyPeopleDead() + + starvationDeaths = (countrymen - (welfareThisYear / 100.0)).toInt() + println("$starvationDeaths COUNTRYMEN DIED OF STARVATION") + } + + var pollutionDeaths = (rnd * (2000 - landArea)).toInt() + if (moneySpentOnPollutionControl >= 25) { + pollutionDeaths = (pollutionDeaths / (moneySpentOnPollutionControl / 25.0)).toInt() + } + + if (pollutionDeaths > 0) { + println("$pollutionDeaths COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION") + } + + death = pollutionDeaths + starvationDeaths + if (death > 0) { + println(" YOU WERE FORCED TO SPEND ${death * 9}") + println("RALLODS ON FUNERAL EXPENSES") + rallods -= death * 9 + } + + if (rallods < 0) { + println(" INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD") + landArea += rallods / landPrice + rallods = 1 + } + + countrymen -= death + + val newForeigners = + if (sellThisYear > 0) { + (sellThisYear + rnd * 10.0 + rnd * 20.0).toInt() + (if (foreignWorkers <= 0) 20 else 0) + } else 0 + + val immigration = ( + (welfareThisYear / 100.0 - countrymen) / 10.0 + + moneySpentOnPollutionControl / 25.0 - + (2000 - landArea) / 50.0 - + pollutionDeaths / 2.0 + ).toInt() + println( + "$newForeigners WORKERS CAME TO THE COUNTRY AND" + + " ${abs(immigration)} COUNTRYMEN ${if (immigration < 0) "LEFT" else "CAME TO"}" + + " THE ISLAND." + ) + + countrymen += immigration + foreignWorkers += newForeigners + + var cropLoss = ((2000 - landArea) * (rnd + 1.5) / 2.0).toInt() + val cropLossWorse = false + if (foreignWorkers > 0) + print("OF $plantingArea SQ. MILES PLANTED,") + if (plantingArea <= cropLoss) + cropLoss = plantingArea + println(" YOU HARVESTED ${plantingArea - cropLoss} SQ. MILES OF CROPS.") + + if (cropLoss > 0) { + println(" (DUE TO ${if (cropLossWorse) "INCREASED " else ""}AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY)") + } + + val agriculturalIncome = ((plantingArea - cropLoss) * landPrice / 2.0).toInt() + println("MAKING $agriculturalIncome RALLODS.") + rallods += agriculturalIncome + + val v1 = (((countrymen - immigration) * 22.0) + rnd * 500).toInt() + val v2 = ((2000.0 - landArea) * 15.0).toInt() + println(" YOU MADE ${abs(v1 - v2)} RALLODS FROM TOURIST TRADE.") + if (v2 != 0 && v1 - v2 < tourists) { + print(" DECREASE BECAUSE ") + println( + when ((10 * rnd).toInt()) { + in 0..2 -> "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." + in 3..4 -> "AIR POLLUTION IS KILLING GAME BIRD POPULATION." + in 5..6 -> "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." + in 7..8 -> "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." + else -> "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." + } + ) + } + + /* + The original code was incorrect. + If v3 starts at 0, for example, our money doubles, when we + have already been told that "YOU MADE ${abs(v1 - v2)} RALLODS + FROM TOURIST TRADE" + + See the original code + 1450 V3=INT(A+V3) + 1451 A=INT(A+V3) + + https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1450%20V3%3DINT,INT(A%2BV3) + */ + if (INCLUDE_BUGS_FROM_ORIGINAL) { + tourists += rallods + } else { + tourists = abs(v1 - v2) + } + rallods += tourists + + if (death > 200) + throw GameEndingException.ExtremeMismanagement(death) + if (countrymen < 343) + throw GameEndingException.TooManyPeopleDead() + if (rallodsAfterPollutionControl / 100 > 5 && death - pollutionDeaths >= 2) + throw GameEndingException.StarvationWithFullTreasury() + if (foreignWorkers > countrymen) + throw GameEndingException.AntiImmigrationRevolution() + + } +} + + +private fun numberInput() = try { + readLine()?.toInt() ?: throw EndOfInputException() +} catch (r: NumberFormatException) { + 0 +} + + + + + From 2d994033c52c3ce42820d8d517863450fb9c0b5c Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Tue, 4 Jan 2022 10:14:09 +1100 Subject: [PATCH 08/26] update docs --- 53_King/kotlin/King.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/53_King/kotlin/King.kt b/53_King/kotlin/King.kt index 2200a9be..01558c99 100644 --- a/53_King/kotlin/King.kt +++ b/53_King/kotlin/King.kt @@ -359,7 +359,7 @@ class GameState(val yearsRequired: Int = 8) { explanationOfSellingGiven = true } } - } while (sellThisYear <= 0 || sellThisYear > landArea - 1000) + } while (sellThisYear < 0 || sellThisYear > landArea - 1000) } /** @@ -376,7 +376,7 @@ class GameState(val yearsRequired: Int = 8) { retry = true } - if (welfareThisYear <= 0) { + if (welfareThisYear < 0) { retry = true } } while (retry) @@ -495,6 +495,14 @@ class GameState(val yearsRequired: Int = 8) { (sellThisYear + rnd * 10.0 + rnd * 20.0).toInt() + (if (foreignWorkers <= 0) 20 else 0) } else 0 + /* + Immigration is calculated as + One for every thousand rallods more welfare than strictly required + minus one for every 10 starvation deaths + plus One for every 25 rallods spent on pollution control + plus one for every 50 square miles of arable land + minus one for every 2 pollution deaths + */ val immigration = ( (welfareThisYear / 100.0 - countrymen) / 10.0 + moneySpentOnPollutionControl / 25.0 - @@ -510,6 +518,12 @@ class GameState(val yearsRequired: Int = 8) { countrymen += immigration foreignWorkers += newForeigners + /* + Crop loss is between 75% and 125% of the land sold to industry, + due to the pollution that industry causes. + Money spent on pollution control reduces pollution deaths among + the population, but does not affect crop losses. + */ var cropLoss = ((2000 - landArea) * (rnd + 1.5) / 2.0).toInt() val cropLossWorse = false if (foreignWorkers > 0) From 4a2217f4be3b586a5cf23098b0ef0de0024d9e38 Mon Sep 17 00:00:00 2001 From: Paul Holt Date: Tue, 4 Jan 2022 10:41:42 +1100 Subject: [PATCH 09/26] Update README.md --- 53_King/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/53_King/README.md b/53_King/README.md index eb0ba8ab..206202f0 100644 --- a/53_King/README.md +++ b/53_King/README.md @@ -8,6 +8,28 @@ The money system is Rollods; each person needs 100 Rallods per year to survive. The author of this program is James A. Storer who wrote it while a student at Lexington High School. +## Bugs + +Implementers should be aware that this game contains at least one bug. + +On basic line 1450 + + 1450 V3=INT(A+V3) + 1451 A=INT(A+V3) + +...where A is the current treasury, and V3 is initially zero. +This would mean that the treasury doubles at the end of the first year, and all calculations for an increase in the treasury due to tourism are discarded. +Possibly, this made the game more playable, although impossible for the player to understand why the treasury was increasing? + +A quick fix for this bug in the original code would be + + 1450 V3=ABS(INT(V1-V2)) + 1451 A=INT(A+V3) + +...judging from the description of tourist income on basic line 1410 + + 1410 PRINT " YOU MADE";ABS(INT(V1-V2));"RALLODS FROM TOURIST TRADE." + --- As published in Basic Computer Games (1978): From 1e9c8008d162d294515ab4b454b29ee008f0c7a8 Mon Sep 17 00:00:00 2001 From: John Long Date: Mon, 3 Jan 2022 16:37:04 -0800 Subject: [PATCH 10/26] Add Kotlin for Synonym This is so much cleaner than the Java :). --- 85_Synonym/kotlin/README.md | 3 ++ 85_Synonym/kotlin/Synonym.kt | 71 ++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 85_Synonym/kotlin/README.md create mode 100644 85_Synonym/kotlin/Synonym.kt diff --git a/85_Synonym/kotlin/README.md b/85_Synonym/kotlin/README.md new file mode 100644 index 00000000..f43a5b70 --- /dev/null +++ b/85_Synonym/kotlin/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Kotlin](https://kotlinlang.org/) diff --git a/85_Synonym/kotlin/Synonym.kt b/85_Synonym/kotlin/Synonym.kt new file mode 100644 index 00000000..94066d03 --- /dev/null +++ b/85_Synonym/kotlin/Synonym.kt @@ -0,0 +1,71 @@ +/** + * Game of Synonym + * + * + * Based on the Basic game of Synonym here + * https://github.com/coding-horror/basic-computer-games/blob/main/85%20Synonym/synonym.bas + * + * + * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing + * new features - no additional text, error checking, etc has been added. + */ + +fun main() { + println(introText) + synonyms.forEach { + // Inside with, "this" is the current synonym + with(it) { + do { + val answer = ask(" WHAT IS A SYNONYM OF $word ? ") + when { + answer == "HELP" -> + println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""") + synonyms.contains(answer) -> + println(RANDOM_ANSWERS.random()) + else -> + println("TRY AGAIN.") + } + } while (!synonyms.contains(answer)) + } + } + println("SYNONYM DRILL COMPLETED.") +} + +val introText = """ +${tab(33)}SYNONYM +${tab(15)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY +A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH +LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME + MEANING. +I CHOOSE A WORD -- YOU TYPE A SYNONYM. +IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP' +AND I WILL TELL YOU A SYNONYM. + + """ + +// prints a question and reads a string (and converts to uppercase) +private fun ask(text: String): String { + print(text) + return readln().uppercase() +} + +// Just like TAB in BASIC +private fun tab(spaces: Int): String = " ".repeat(spaces) + +val RANDOM_ANSWERS = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK") + +// List of words and synonyms +private val synonyms = listOf( + SynonymList("FIRST", listOf("START", "BEGINNING", "ONSET", "INITIAL")), + SynonymList("SIMILAR", listOf("SAME", "LIKE", "RESEMBLING")), + SynonymList("MODEL", listOf("PATTERN", "PROTOTYPE", "STANDARD", "CRITERION")), + SynonymList("SMALL", listOf("INSIGNIFICANT", "LITTLE", "TINY", "MINUTE")), + SynonymList("STOP", listOf("HALT", "STAY", "ARREST", "CHECK", "STANDSTILL")), + SynonymList("HOUSE", listOf("DWELLING", "RESIDENCE", "DOMICILE", "LODGING", "HABITATION")), + SynonymList("PIT", listOf("HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS")), + SynonymList("PUSH", listOf("SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS")), + SynonymList("RED", listOf("ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY")), + SynonymList("PAIN", listOf("SUFFERING", "HURT", "MISERY", "DISTRESS", "ACHE", "DISCOMFORT")) +) + +class SynonymList(val word: String, val synonyms: List) \ No newline at end of file From 8be28cd39a3ee3a642a047a8e303f70a24b1e5a4 Mon Sep 17 00:00:00 2001 From: John Long Date: Mon, 3 Jan 2022 16:45:26 -0800 Subject: [PATCH 11/26] A little cleaner implementation --- 85_Synonym/kotlin/Synonym.kt | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/85_Synonym/kotlin/Synonym.kt b/85_Synonym/kotlin/Synonym.kt index 94066d03..d2e68dc4 100644 --- a/85_Synonym/kotlin/Synonym.kt +++ b/85_Synonym/kotlin/Synonym.kt @@ -13,23 +13,25 @@ fun main() { println(introText) synonyms.forEach { - // Inside with, "this" is the current synonym - with(it) { - do { - val answer = ask(" WHAT IS A SYNONYM OF $word ? ") - when { - answer == "HELP" -> - println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""") - synonyms.contains(answer) -> - println(RANDOM_ANSWERS.random()) - else -> - println("TRY AGAIN.") - } - } while (!synonyms.contains(answer)) - } + it.testUser() } println("SYNONYM DRILL COMPLETED.") } +// We could put this inside of SynonymList, but this keeps the core implementation +// right here at the top +private fun SynonymList.testUser() { + do { + val answer = ask(" WHAT IS A SYNONYM OF $word ? ") + when { + answer == "HELP" -> + println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""") + synonyms.contains(answer) -> + println(RANDOM_ANSWERS.random()) + else -> + println("TRY AGAIN.") + } + } while (!synonyms.contains(answer)) +} val introText = """ ${tab(33)}SYNONYM From 1520a8f225ec55582cb4ff9070389fab56c9e1f5 Mon Sep 17 00:00:00 2001 From: John Long Date: Mon, 3 Jan 2022 16:47:47 -0800 Subject: [PATCH 12/26] Affirmation more accurate than Answer --- 85_Synonym/kotlin/Synonym.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/85_Synonym/kotlin/Synonym.kt b/85_Synonym/kotlin/Synonym.kt index d2e68dc4..dda96a82 100644 --- a/85_Synonym/kotlin/Synonym.kt +++ b/85_Synonym/kotlin/Synonym.kt @@ -26,7 +26,7 @@ private fun SynonymList.testUser() { answer == "HELP" -> println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""") synonyms.contains(answer) -> - println(RANDOM_ANSWERS.random()) + println(RANDOM_AFFIRMATION.random()) else -> println("TRY AGAIN.") } @@ -54,7 +54,7 @@ private fun ask(text: String): String { // Just like TAB in BASIC private fun tab(spaces: Int): String = " ".repeat(spaces) -val RANDOM_ANSWERS = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK") +val RANDOM_AFFIRMATION = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK") // List of words and synonyms private val synonyms = listOf( From 916fa6f252188b1fa8a9bb605eda1d29569defb0 Mon Sep 17 00:00:00 2001 From: John Long Date: Mon, 3 Jan 2022 16:48:49 -0800 Subject: [PATCH 13/26] And now RANDOM is redundant --- 85_Synonym/kotlin/Synonym.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/85_Synonym/kotlin/Synonym.kt b/85_Synonym/kotlin/Synonym.kt index dda96a82..386a2525 100644 --- a/85_Synonym/kotlin/Synonym.kt +++ b/85_Synonym/kotlin/Synonym.kt @@ -26,7 +26,7 @@ private fun SynonymList.testUser() { answer == "HELP" -> println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""") synonyms.contains(answer) -> - println(RANDOM_AFFIRMATION.random()) + println(AFFIRMATIONS.random()) else -> println("TRY AGAIN.") } @@ -54,7 +54,7 @@ private fun ask(text: String): String { // Just like TAB in BASIC private fun tab(spaces: Int): String = " ".repeat(spaces) -val RANDOM_AFFIRMATION = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK") +val AFFIRMATIONS = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK") // List of words and synonyms private val synonyms = listOf( From 2c1dea4de7cef0c0e8f6b0a3843b55396af0658f Mon Sep 17 00:00:00 2001 From: a2wd Date: Tue, 4 Jan 2022 01:57:36 +0100 Subject: [PATCH 14/26] Added bombardment in c# --- 11_Bombardment/csharp/Bombardment.cs | 183 +++++++++++++++++++++++ 11_Bombardment/csharp/Bombardment.csproj | 8 + 11_Bombardment/csharp/Program.cs | 13 ++ 3 files changed, 204 insertions(+) create mode 100644 11_Bombardment/csharp/Bombardment.cs create mode 100644 11_Bombardment/csharp/Bombardment.csproj create mode 100644 11_Bombardment/csharp/Program.cs diff --git a/11_Bombardment/csharp/Bombardment.cs b/11_Bombardment/csharp/Bombardment.cs new file mode 100644 index 00000000..bcbdcc26 --- /dev/null +++ b/11_Bombardment/csharp/Bombardment.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; + +namespace Bombardment +{ + // + // Game of Bombardment + // Based on the Basic game of Bombardment here + // https://github.com/coding-horror/basic-computer-games/blob/main/11%20Bombardment/bombardment.bas + // Note: The idea was to create a version of the 1970's Basic game in C#, without introducing + // new features - no additional text, error checking, etc has been added. + // + internal class Bombardment + { + private static int MAX_GRID_SIZE = 25; + private static int MAX_PLATOONS = 4; + private static Random random = new Random(); + private List computerPositions = new List(); + private List playerPositions = new List(); + private List computerGuesses = new List(); + + private void PrintStartingMessage() + { + Console.WriteLine("{0}BOMBARDMENT", new string(' ', 33)); + Console.WriteLine("{0}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", new string(' ', 15)); + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); + + Console.WriteLine("YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU"); + Console.WriteLine("HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED."); + Console.WriteLine("YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST."); + Console.WriteLine("THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS."); + Console.WriteLine(); + Console.WriteLine("THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE"); + Console.WriteLine("OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU."); + Console.WriteLine("THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS"); + Console.WriteLine("FIRST IS THE WINNER."); + Console.WriteLine(); + Console.WriteLine("GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!"); + Console.WriteLine(); + Console.WriteLine("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS."); + + // As an alternative to repeating the call to WriteLine(), + // we can print the new line character five times. + Console.Write(new string('\n', 5)); + + // Print a sample board (presumably the game was originally designed to be + // physically printed on paper while played). + for (var i = 1; i <= 25; i += 5) + { + // The token replacement can be padded by using the format {tokenPosition, padding} + // Negative values for the padding cause the output to be left-aligned. + Console.WriteLine("{0,-3}{1,-3}{2,-3}{3,-3}{4,-3}", i, i + 1, i + 2, i + 3, i + 4); + } + + Console.WriteLine("\n"); + } + + // Generate 5 random positions for the computer's platoons. + private void PlaceComputerPlatoons() + { + do + { + var nextPosition = random.Next(1, MAX_GRID_SIZE); + if (!computerPositions.Contains(nextPosition)) + { + computerPositions.Add(nextPosition); + } + + } while (computerPositions.Count < MAX_PLATOONS); + } + + private void StoreHumanPositions() + { + Console.WriteLine("WHAT ARE YOUR FOUR POSITIONS"); + + // The original game assumed that the input would be five comma-separated values, all on one line. + // For example: 12,22,1,4,17 + var input = Console.ReadLine(); + var playerPositionsAsStrings = input.Split(","); + foreach (var playerPosition in playerPositionsAsStrings) { + playerPositions.Add(int.Parse(playerPosition)); + } + } + + private void HumanTurn() + { + Console.WriteLine("WHERE DO YOU WISH TO FIRE YOUR MISSLE"); + var input = Console.ReadLine(); + var humanGuess = int.Parse(input); + + if(computerPositions.Contains(humanGuess)) + { + Console.WriteLine("YOU GOT ONE OF MY OUTPOSTS!"); + computerPositions.Remove(humanGuess); + + switch(computerPositions.Count) + { + case 3: + Console.WriteLine("ONE DOWN, THREE TO GO."); + break; + case 2: + Console.WriteLine("TWO DOWN, TWO TO GO."); + break; + case 1: + Console.WriteLine("THREE DOWN, ONE TO GO."); + break; + case 0: + Console.WriteLine("YOU GOT ME, I'M GOING FAST."); + Console.WriteLine("BUT I'LL GET YOU WHEN MY TRANSISTO&S RECUP%RA*E!"); + break; + } + } + else + { + Console.WriteLine("HA, HA YOU MISSED. MY TURN NOW:"); + } + } + + private int GenerateComputerGuess() + { + int computerGuess; + do + { + computerGuess = random.Next(1, 25); + } + while(computerGuesses.Contains(computerGuess)); + computerGuesses.Add(computerGuess); + + return computerGuess; + } + + private void ComputerTurn() + { + var computerGuess = GenerateComputerGuess(); + + if (playerPositions.Contains(computerGuess)) + { + Console.WriteLine("I GOT YOU. IT WON'T BE LONG NOW. POST {0} WAS HIT.", computerGuess); + playerPositions.Remove(computerGuess); + + switch(playerPositions.Count) + { + case 3: + Console.WriteLine("YOU HAVE ONLY THREE OUTPOSTS LEFT."); + break; + case 2: + Console.WriteLine("YOU HAVE ONLY TWO OUTPOSTS LEFT."); + break; + case 1: + Console.WriteLine("YOU HAVE ONLY ONE OUTPOST LEFT."); + break; + case 0: + Console.WriteLine("YOU'RE DEAD. YOUR LAST OUTPOST WAS AT {0}. HA, HA, HA.", computerGuess); + Console.WriteLine("BETTER LUCK NEXT TIME."); + break; + } + } + else + { + Console.WriteLine("I MISSED YOU, YOU DIRTY RAT. I PICKED {0}. YOUR TURN:", computerGuess); + } + } + + public void Play() + { + PrintStartingMessage(); + PlaceComputerPlatoons(); + StoreHumanPositions(); + + while (playerPositions.Count > 0 && computerPositions.Count > 0) + { + HumanTurn(); + + if (computerPositions.Count > 0) + { + ComputerTurn(); + } + } + } + } +} diff --git a/11_Bombardment/csharp/Bombardment.csproj b/11_Bombardment/csharp/Bombardment.csproj new file mode 100644 index 00000000..e5e0e164 --- /dev/null +++ b/11_Bombardment/csharp/Bombardment.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp2.1 + + + diff --git a/11_Bombardment/csharp/Program.cs b/11_Bombardment/csharp/Program.cs new file mode 100644 index 00000000..acc438ba --- /dev/null +++ b/11_Bombardment/csharp/Program.cs @@ -0,0 +1,13 @@ +using System; + +namespace Bombardment +{ + class Program + { + static void Main(string[] args) + { + var bombardment = new Bombardment(); + bombardment.Play(); + } + } +} From e8849566baf01424909f797d9db7db36e432b0c2 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 4 Jan 2022 12:35:51 +1100 Subject: [PATCH 15/26] fix(amazing): guarantee exit on bottom row of maze This issue only tends to show up on very small mazes (e.g. 2x2, 3x3). It is possible for the algorithm to never generate an exit to the maze. While the algorithm guarantees with the `Z` variable that only one exit will be generated, it does not test for the situation where we just happen to never get the right random value to open an exit on the bottom row. The simplest resolution is just to check for this before rendering the final result (i.e. `IF Z=0`), and add an exit to a random cell on the bottom row. --- 02_Amazing/amazing.bas | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/02_Amazing/amazing.bas b/02_Amazing/amazing.bas index 63255319..3b39c93a 100644 --- a/02_Amazing/amazing.bas +++ b/02_Amazing/amazing.bas @@ -117,10 +117,15 @@ 975 V(R,S)=3:Q=0:GOTO 1000 980 V(R,S)=1:Q=0:R=1:S=1:GOTO 250 1000 GOTO 210 -1010 FOR J=1 TO V -1011 PRINT "I"; -1012 FOR I=1 TO H -1013 IF V(I,J)<2 THEN 1030 +1010 IF Z=1 THEN 1015 +1011 X=INT(RND(1)*H+1) +1012 IF V(X,V)=0 THEN 1014 +1013 V(X,V)=3: GOTO 1015 +1014 V(X,V)=1 +1015 FOR J=1 TO V +1016 PRINT "I"; +1017 FOR I=1 TO H +1018 IF V(I,J)<2 THEN 1030 1020 PRINT " "; 1021 GOTO 1040 1030 PRINT " I"; From 321205cc7769f75671028fc3190b1aa45b338b78 Mon Sep 17 00:00:00 2001 From: Alaa Sarhan Date: Tue, 4 Jan 2022 02:49:50 +0100 Subject: [PATCH 16/26] minor changes --- 13_Bounce/ruby/bounce.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/13_Bounce/ruby/bounce.rb b/13_Bounce/ruby/bounce.rb index 339c7851..e215a897 100644 --- a/13_Bounce/ruby/bounce.rb +++ b/13_Bounce/ruby/bounce.rb @@ -110,8 +110,8 @@ def plot_bouncing_ball(strobbing_time, v0, c) } if heighest_position_in_next_bounce(time_in_bounce, v0, i, c) < plotted_height then - # If we got no more ball positions at or above current height, we can skip - # the rest of the bounces and move down to the next height to plot + # If we got no more ball positions at or above current height in the next bounce, + # we can skip the rest of the bounces and move down to the next height to plot puts break end @@ -170,11 +170,11 @@ def game_loop end -## Game entry point +## Entry point begin intro - while true + loop do game_loop end rescue SystemExit, Interrupt From f98c8af9f82a3cfe36c5ca2c18f3b70af6b94149 Mon Sep 17 00:00:00 2001 From: RibTips <36372030+ribtips@users.noreply.github.com> Date: Tue, 4 Jan 2022 00:21:38 -0500 Subject: [PATCH 17/26] Perl version of 35_evenwins --- 35_Even_Wins/perl/evenwins.pl | 168 ++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 35_Even_Wins/perl/evenwins.pl diff --git a/35_Even_Wins/perl/evenwins.pl b/35_Even_Wins/perl/evenwins.pl new file mode 100644 index 00000000..20065c23 --- /dev/null +++ b/35_Even_Wins/perl/evenwins.pl @@ -0,0 +1,168 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +&main; + +sub main { + &print_intro; + &game_play; +} + +sub game_play { + my $marbles = 27; + my $turn = 0; + my $player_total = 0; + my $computer_total = 0; + print "TYPE A '1' IF YOU WANT TO GO FIRST AND TYPE A '0' IF YOU WANT ME TO GO FIRST\n"; + my $choice = ; + chomp($choice); + if ($choice == 0) { + until ($marbles == 0) { + + my $computer_choice = &computer_select($marbles,$turn); + $marbles = $marbles - $computer_choice; + $computer_total = $computer_total + $computer_choice; + print "MY TOTAL IS $computer_total\n"; + + print "TOTAL= $marbles\n"; + + if ($marbles == 0) {&determine_winner($computer_total,$player_total)}; + + my $player_choice = &player_select($marbles,$turn); + $marbles = $marbles - $player_choice; + $player_total = $player_total + $player_choice; + print "YOUR TOTAL IS $player_total\n"; + $turn++; + print "TOTAL= $marbles\n"; + if ($marbles == 0) {&determine_winner($computer_total,$player_total)}; + } + } + elsif ($choice == 1) { + until ($marbles == 0) { + + my $player_choice = &player_select($marbles,$turn); + $marbles = $marbles - $player_choice; + $player_total = $player_total + $player_choice; + $turn++; + print "YOUR TOTAL IS $player_total\n"; + + print "TOTAL= $marbles\n"; + + if ($marbles == 0) {&determine_winner($computer_total,$player_total)}; + + my $computer_choice = &computer_select($marbles,$turn); + $marbles = $marbles - $computer_choice; + $computer_total = $computer_total + $computer_choice; + print "MY TOTAL IS $computer_total\n"; + + print "TOTAL= $marbles\n"; + + if ($marbles == 0) {&determine_winner($computer_total,$player_total)}; + + } + } +} + +sub determine_winner { + my $computer = shift; + my $player = shift; + print "THAT IS ALL OF THE MARBLES.\n\n"; + print "MY TOTAL IS $computer, YOUR TOTAL IS $player\n"; + if ($player % 2 == 0) { + print " YOU WON.\n"; + } + if ($computer % 2 == 0) { + print " I WON.\n"; + } + my $answer = -1; + until ($answer == 1 || $answer == 0) { + print "DO YOU WANT TO PLAY AGAIN? TYPE 1 FOR YES AND 0 FOR NO.\n"; + $answer=; + chomp($answer); + } + if ($answer == 1) { + &game_play; + } + else { + print "OK. SEE YOU LATER.\n"; + exit; + } +} + +sub player_select { + my $marbles = shift; + my $turn = shift; + my $validity="invalid"; + if ($turn == 0) { + print "WHAT IS YOUR FIRST MOVE\n"; + } + else { + print "WHAT IS YOUR NEXT MOVE\n"; + } + until ($validity eq "valid") { + my $num = ; + chomp($num); + my $validity=&validity_check($marbles,$num); + if ($validity eq "valid") { + return $num; + } + } +} + +sub computer_select { + my $marbles = shift; + my $turn = shift; + my $num = 2; + my $validity = "invalid"; + if ($turn == 0) { + print "I PICK UP $num MARBLES.\n\n"; + } + else { + until ($validity eq "valid") { + my $R=$marbles-6*int(($marbles/6)); + if ($marbles < 4.2) { + $num = $marbles; + } + if ($R > 3.4) { + if ($R < 4.7 || $R > 3.5) { + $num = 4; + } + else { + $num = 1; + } + } + $validity=&validity_check($marbles,$num); + } + print "\nI PICK UP $num MARBLES.\n\n"; + } + return $num; +} + +sub validity_check { + my $marbles = shift; + my $num = shift; + if ($num > $marbles) { + print "YOU HAVE TRIED TO TAKE MORE MARBLES THAN THERE ARE LEFT. TRY AGAIN. THERE ARE $marbles MARBLES LEFT\n"; + return "invalid"; + } + if ($num > 0 && $num <= 4) { + return "valid"; + } + if ($num < 1 || $num > 4) { + print "THE NUMBER OF MARBLES YOU TAKE MUST BE A POSITIVE INTEGER BETWEEN 1 AND 4\nWHAT IS YOUR NEXT MOVE?\n"; + return "invalid"; + } + else { + return "invalid"; + } +} + +sub print_intro { + print ' ' x 31 . "EVEN WINS\n"; + print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + print "THIS IS A 2 PERSON GAME CALLED 'EVEN WINS'. TO PLAY THE GAME, THE PLAYERS NEED 27 MARBLES OR OTHER OBJECTS ON THE TABLE\n\n"; + print "THE 2 PLAYERS ALTERNATE TURNS, WITH EACH PLAYER REMOVING FROM 1 TO 4 MARBLES ON EACH MOVE. THE GAME ENDS WHEN THERE ARE NO MARBLES LEFT, AND THE WINNER IS THE ONE WITH AN EVEN NUMBER OF MARBLES\n"; + print "THE ONLY RULES ARE THAT (1) YOU MUST ALTERNATE TURNS, (2) YOU MUST TAKE BETWEEN 1 AND 4 MARBLES EACH TURN, AND (3) YOU CANNOT SKIP A TURN.\n\n"; +} From d82a637152968ff3dd6e035965b6bf65f1a228de Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 4 Jan 2022 16:47:32 +1100 Subject: [PATCH 18/26] chore(amazing): add note to README.md for #400 --- 02_Amazing/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/02_Amazing/README.md b/02_Amazing/README.md index 8f7b271b..e94949bd 100644 --- a/02_Amazing/README.md +++ b/02_Amazing/README.md @@ -12,3 +12,7 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html + +--- + +**2022-01-04:** patched original source in [#400](https://github.com/coding-horror/basic-computer-games/pull/400) to fix a minor bug where a generated maze may be missing an exit, particularly at small maze sizes. From 55b3c0d4e76142312e9abf2276b51b097aa5631d Mon Sep 17 00:00:00 2001 From: RibTips <36372030+ribtips@users.noreply.github.com> Date: Tue, 4 Jan 2022 01:33:11 -0500 Subject: [PATCH 19/26] Add files via upload --- 35_Even_Wins/perl/evenwins.pl | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/35_Even_Wins/perl/evenwins.pl b/35_Even_Wins/perl/evenwins.pl index 20065c23..c2fdc2d8 100644 --- a/35_Even_Wins/perl/evenwins.pl +++ b/35_Even_Wins/perl/evenwins.pl @@ -21,7 +21,7 @@ sub game_play { if ($choice == 0) { until ($marbles == 0) { - my $computer_choice = &computer_select($marbles,$turn); + my $computer_choice = &computer_select($marbles,$turn,$player_total); $marbles = $marbles - $computer_choice; $computer_total = $computer_total + $computer_choice; print "MY TOTAL IS $computer_total\n"; @@ -52,7 +52,7 @@ sub game_play { if ($marbles == 0) {&determine_winner($computer_total,$player_total)}; - my $computer_choice = &computer_select($marbles,$turn); + my $computer_choice = &computer_select($marbles,$turn,$player_total); $marbles = $marbles - $computer_choice; $computer_total = $computer_total + $computer_choice; print "MY TOTAL IS $computer_total\n"; @@ -114,6 +114,7 @@ sub player_select { sub computer_select { my $marbles = shift; my $turn = shift; + my $player_total = shift; my $num = 2; my $validity = "invalid"; if ($turn == 0) { @@ -122,14 +123,24 @@ sub computer_select { else { until ($validity eq "valid") { my $R=$marbles-6*int(($marbles/6)); - if ($marbles < 4.2) { + + if (int($player_total/2) == $player_total/2) { + if ($R < 1.5 || $R > 5.3) { + $num = 1; + } + else { + $num = $R - 1; + } + } + + elsif ($marbles < 4.2) { $num = $marbles; } - if ($R > 3.4) { + elsif ($R > 3.4) { if ($R < 4.7 || $R > 3.5) { $num = 4; } - else { + else { $num = 1; } } From 41183198a8d9e57135b079c48923ef7207d87404 Mon Sep 17 00:00:00 2001 From: RibTips <36372030+ribtips@users.noreply.github.com> Date: Tue, 4 Jan 2022 01:37:57 -0500 Subject: [PATCH 20/26] added logic/intelligence into the AI modified the logic from how the computer was making its selections. Original code had a simple random number, this incorporates the original logic from the BASIC game. --- 35_Even_Wins/python/evenwins.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/35_Even_Wins/python/evenwins.py b/35_Even_Wins/python/evenwins.py index fba20024..3fb9d4af 100644 --- a/35_Even_Wins/python/evenwins.py +++ b/35_Even_Wins/python/evenwins.py @@ -142,17 +142,32 @@ def game_over(): print('') def computer_turn(): - global marbles_in_middle - global computer_marbles + global marbles_in_middle + global computer_marbles + global human_marbles - print("It's the computer's turn ...") - max_choice = min(4, marbles_in_middle) + marbles_to_take=0 - # choose at random - n = random.randint(1, max_choice) - print(f'Computer takes {marbles_str(n)} ...') - marbles_in_middle -= n - computer_marbles += n + print("It's the computer's turn ...") + r = marbles_in_middle - 6 * int((marbles_in_middle/6)) #line 500 + + if int(human_marbles/2) == human_marbles/2: #line 510 + if r < 1.5 or r > 5.3: #lines 710 and 720 + marbles_to_take = 1 + else: + marbles_to_take = r - 1 + + elif marbles_in_middle < 4.2: #line 580 + marbles_to_take = marbles_in_middle + elif r > 3.4: #line 530 + if r < 4.7 or r > 3.5: + marbles_to_take = 4 + else: + marbles_to_take = r + 1 + + print(f'Computer takes {marbles_str(marbles_to_take)} ...') + marbles_in_middle -= marbles_to_take + computer_marbles += marbles_to_take def play_game(): global marbles_in_middle From ec43a4fc9490c220e76b434c045b3c84198eeefd Mon Sep 17 00:00:00 2001 From: Bastiaan Veelo Date: Mon, 3 Jan 2022 00:19:19 +0100 Subject: [PATCH 21/26] D version of Acey-Ducey. --- 01_Acey_Ducey/d/.gitignore | 2 + 01_Acey_Ducey/d/README.md | 29 ++++++ 01_Acey_Ducey/d/aceyducey.d | 131 ++++++++++++++++++++++++++++ 01_Acey_Ducey/d/aceyducey_literal.d | 104 ++++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 01_Acey_Ducey/d/.gitignore create mode 100644 01_Acey_Ducey/d/README.md create mode 100644 01_Acey_Ducey/d/aceyducey.d create mode 100644 01_Acey_Ducey/d/aceyducey_literal.d diff --git a/01_Acey_Ducey/d/.gitignore b/01_Acey_Ducey/d/.gitignore new file mode 100644 index 00000000..d969f6b2 --- /dev/null +++ b/01_Acey_Ducey/d/.gitignore @@ -0,0 +1,2 @@ +*.exe +*.obj diff --git a/01_Acey_Ducey/d/README.md b/01_Acey_Ducey/d/README.md new file mode 100644 index 00000000..a45fcb2c --- /dev/null +++ b/01_Acey_Ducey/d/README.md @@ -0,0 +1,29 @@ +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). + +Two versions are supplied that are functionally equivalent, but differ in source layout: + +
+
aceyducey_literal.d
+
A largely literal transcription of the original Basic source. All unnecessary uglyness is preserved.
+
aceyducey.d
+
An idiomatic D refactoring of the original, with a focus on increasing the readability and robustness. + Memory-safety is ensured by the language, thanks to the + @safe annotation.
+
+ +## Running the code + +Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler: +```shell +dmd -run aceyducey.d +``` + +[Other compilers](https://dlang.org/download.html) also exist. + +Note that there are compiler switches related to memory-safety (`-preview=dip25` and `-preview=dip1000`) that are not +used here because they are unnecessary in this case. What these do is to make the analysis more thorough, so that with +them some code that needed to be `@system` can then be inferred to be in fact `@safe`. [Code that compiles without +these switches is just as safe as when compiled with them] +(https://forum.dlang.org/post/dftgjalswvwfjpyushgn@forum.dlang.org). diff --git a/01_Acey_Ducey/d/aceyducey.d b/01_Acey_Ducey/d/aceyducey.d new file mode 100644 index 00000000..036786c5 --- /dev/null +++ b/01_Acey_Ducey/d/aceyducey.d @@ -0,0 +1,131 @@ +@safe: // Make @safe the default for this file, enforcing memory-safety. + +void main() +{ + import std.stdio : write, writeln; + import std.string : center, toUpper, wrap; + import std.exception : ifThrown; + + enum width = 80; + writeln(center("Acey Ducey Card Game", width)); + writeln(center("(After Creative Computing Morristown, New Jersey)\n", width)); + writeln(wrap("Acey-Ducey is played in the following manner: The dealer (computer) deals two cards face up. " ~ + "You have an option to bet or not bet depending on whether or not you feel the third card will " ~ + "have a value between the first two. If you do not want to bet, input a 0.", width)); + + enum Hand {low, middle, high} + Card[Hand.max + 1] cards; // Three cards. + bool play = true; + + while (play) + { + int cash = 100; + while (cash > 0) + { + writeln("\nYou now have ", cash, " dollars."); + int bet = 0; + while (bet <= 0) + { + do // Draw new cards, until the first card has a smaller value than the last card. + { + foreach (ref card; cards) + card.drawNew; + } while (cards[Hand.low] >= cards[Hand.high]); + writeln("Here are your next two cards:\n", cards[Hand.low], "\n", cards[Hand.high]); + + int askBet() // A nested function. + { + import std.conv : to; + + write("\nWhat is your bet? "); + int answer = readString.to!int. + ifThrown!Exception(askBet); // Try again when answer does not convert to int. + if (answer <= cash) + return answer; + writeln("Sorry, my friend, but you bet too much.\nYou have only ", cash, " dollars to bet."); + return askBet; // Recurse: Ask again. + } + bet = askBet; + if (bet <= 0) // Negative bets are interpreted as 0. + writeln("CHICKEN!!"); + } // bet is now > 0. + + writeln(cards[Hand.middle]); + if (cards[Hand.low] < cards[Hand.middle] && cards[Hand.middle] < cards[Hand.high]) + { + writeln("YOU WIN!!!"); + cash += bet; + } + else + { + writeln("Sorry, you lose."); + cash -= bet; + if (cash <= 0) + { + writeln("\n\nSorry, friend, but you blew your wad."); + write("\n\nTry again (Yes or No)? "); + play = readString.toUpper == "YES"; + } + } + } + } + writeln("O.K., hope you had fun!"); +} + +struct Card +{ + int value = 2; + alias value this; // Enables Card to stand in as an int, so that cards can be compared as ints. + + invariant + { + assert(2 <= value && value <= 14); // Ensure cards always have a valid value. + } + + /// Adopt a new value. + void drawNew() + { + import std.random : uniform; + + value = uniform!("[]", int, int)(2, 14); // A random int between inclusive bounds. + } + + /// Called for implicit conversion to string. + string toString() const pure + { + import std.conv : text; + + switch (value) + { + case 11: return "Jack"; + case 12: return "Queen"; + case 13: return "King"; + case 14: return "Ace"; + default: return text(" ", value); // Basic prepends a space. + } + } +} + +/// Read a string from standard input, stripping newline and other enclosing whitespace. +string readString() nothrow +{ + import std.string : strip; + + 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 +{ + import std.stdio : readln; + + return readln; +} diff --git a/01_Acey_Ducey/d/aceyducey_literal.d b/01_Acey_Ducey/d/aceyducey_literal.d new file mode 100644 index 00000000..a51028de --- /dev/null +++ b/01_Acey_Ducey/d/aceyducey_literal.d @@ -0,0 +1,104 @@ +void main() +{ + import std; + + L10: writef("%26s", ' '); writeln("ACEY DUCEY CARD GAME"); + L20: writef("%15s", ' '); writeln("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + L21: writeln; + L22: writeln; + L30: writeln("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER "); + L40: writeln("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP"); + L50: writeln("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING"); + L60: writeln("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE"); + L70: writeln("A VALUE BETWEEN THE FIRST TWO."); + L80: writeln("IF YOU DO NOT WANT TO BET, INPUT A 0"); + L100: int N=100; + L110: int Q=100, M; + L120: writeln("YOU NOW HAVE ",Q," DOLLARS."); + L130: writeln; + L140: goto L260; + L210: Q=Q+M; + L220: goto L120; + L240: Q=Q-M; + L250: goto L120; + L260: writeln("HERE ARE YOUR NEXT TWO CARDS: "); + L270: auto A=to!int(14*uniform01)+2; + L280: if (A<2) goto L270; + L290: if (A>14) goto L270; + L300: auto B=to!int(14*uniform01)+2; + L310: if (B<2) goto L300; + L320: if (B>14) goto L300; + L330: if (A>=B) goto L270; + L350: if (A<11) goto L400; + L360: if (A==11) goto L420; + L370: if (A==12) goto L440; + L380: if (A==13) goto L460; + L390: if (A==14) goto L480; + L400: writefln("%2d", A); + L410: goto L500; + L420: writeln("JACK"); + L430: goto L500; + L440: writeln("QUEEN"); + L450: goto L500; + L460: writeln("KING"); + L470: goto L500; + L480: writeln("ACE"); + L500: if (B<11) goto L550; + L510: if (B==11) goto L570; + L520: if (B==12) goto L590; + L530: if (B==13) goto L610; + L540: if (B==14) goto L630; + L550: writefln("%2d", B); + L560: goto L650; + L570: writeln("JACK"); + L580: goto L650; + L590: writeln("QUEEN"); + L600: goto L650; + L610: writeln("KING"); + L620: goto L650; + L630: writeln("ACE"); + L640: writeln; + L650: writeln; + L660: write("WHAT IS YOUR BET? "); M = stdin.readln.strip.to!int; + L670: if (M!=0) goto L680; + L675: writeln("CHICKEN!!"); + L676: writeln; + L677: goto L260; + L680: if (M<=Q) goto L730; + L690: writeln("SORRY, MY FRIEND, BUT YOU BET TOO MUCH."); + L700: writeln("YOU HAVE ONLY ",Q," DOLLARS TO BET."); + L710: goto L650; + L730: auto C=to!int(14*uniform01)+2; + L740: if (C<2) goto L730; + L750: if (C>14) goto L730; + L760: if (C<11) goto L810; + L770: if (C==11) goto L830; + L780: if (C==12) goto L850; + L790: if (C==13) goto L870; + L800: if (C==14) goto L890; + L810: writeln(C); + L820: goto L910; + L830: writeln("JACK"); + L840: goto L910; + L850: writeln("QUEEN"); + L860: goto L910; + L870: writeln("KING"); + L880: goto L910; + L890: writeln( "ACE"); + L900: writeln; + L910: if (C>A) goto L930; + L920: goto L970; + L930: if (C>=B) goto L970; + L950: writeln("YOU WIN!!!"); + L960: goto L210; + L970: writeln("SORRY, YOU LOSE"); + L980: if (M Date: Tue, 4 Jan 2022 08:36:37 -0500 Subject: [PATCH 22/26] cleanup --- 31_Depth_Charge/ruby/.gitignore | 57 +++++++++++++++++++++++++++++ 31_Depth_Charge/ruby/depthcharge.rb | 48 ++++-------------------- 2 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 31_Depth_Charge/ruby/.gitignore diff --git a/31_Depth_Charge/ruby/.gitignore b/31_Depth_Charge/ruby/.gitignore new file mode 100644 index 00000000..d25ad7bc --- /dev/null +++ b/31_Depth_Charge/ruby/.gitignore @@ -0,0 +1,57 @@ +# Package Shell Specific Things + +## Build Process + +/build + +## Transient files + +/src/input +/src/output +/src/log +/drop + +# Programming Languages + +## Java +/src/java/**/*.class + +## Rakudo / Perl6 +/src/**/.precomp + +## PHP +/vendor/ + +## Python +*.swp +__pycache__/ +*.pyc +*.egg-info +/dist + +## Ruby +*.gem +.bundle + +# Editors + +## Dia +*.dia.autosave + +## Emacs +\#*\# +.\#* + + +## Vi / Vim +*.swp + +# Filesystem Artifacts + +## Mac + +.DS_Store + +## NFS + +.nfs* diff --git a/31_Depth_Charge/ruby/depthcharge.rb b/31_Depth_Charge/ruby/depthcharge.rb index 5ba36ba6..ba2a5d83 100755 --- a/31_Depth_Charge/ruby/depthcharge.rb +++ b/31_Depth_Charge/ruby/depthcharge.rb @@ -13,7 +13,6 @@ class DepthCharge break if ! get_input_another_game() end - # 420 PRINT "OK. HOPE YOU ENJOYED YOURSELF." : GOTO 600 printf("OK. HOPE YOU ENJOYED YOURSELF.\n") end @@ -51,7 +50,7 @@ class DepthCharge the_input = Integer(value) rescue nil - if the_input == nil || the_input < 0 + if the_input == nil || the_input < 1 printf("PLEASE ENTER A POSITIVE NUMBER\n\n") next @@ -61,25 +60,7 @@ class DepthCharge 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 @@ -110,19 +91,21 @@ GOOD LUCK ! end def setup_game - get_search_area_dimension() + @search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ") + + @num_tries = Integer( + Math.log(@search_area_dimension)/Math.log(2) + 1 + ) setup_enemy() 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 + 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() @@ -130,7 +113,6 @@ GOOD LUCK ! @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 \ @@ -140,7 +122,6 @@ GOOD LUCK ! you_win() return else - # 140 GOSUB 500 : PRINT : NEXT D missed_shot() end end @@ -156,54 +137,41 @@ GOOD LUCK ! printf("TRIAL \#%d\n", @trial) end def you_win - printf("B O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial ) + printf("\nB 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 @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 @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 @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 From a05a1fedf37285e5e087138f3a529a2879d7f888 Mon Sep 17 00:00:00 2001 From: "christopher.millward" Date: Tue, 4 Jan 2022 10:40:59 -0500 Subject: [PATCH 23/26] War refactor and cleanup --- 94_War/javascript/war.js | 228 ++++++++++++++++++++++----------------- 1 file changed, 128 insertions(+), 100 deletions(-) diff --git a/94_War/javascript/war.js b/94_War/javascript/war.js index eb74fb0c..bdc935bc 100644 --- a/94_War/javascript/war.js +++ b/94_War/javascript/war.js @@ -1,60 +1,95 @@ // WAR // -// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess) +// Original conversion from BASIC to Javascript by Oscar Toledo G. (nanochess) // -function print(str) -{ +function print(str) { document.getElementById("output").appendChild(document.createTextNode(str)); } -function input() -{ - var input_element; - var input_str; - - return new Promise(function (resolve) { - input_element = document.createElement("INPUT"); - - print("? "); - input_element.setAttribute("type", "text"); - input_element.setAttribute("length", "50"); - document.getElementById("output").appendChild(input_element); - input_element.focus(); - input_str = undefined; - input_element.addEventListener("keydown", function (event) { - if (event.keyCode == 13) { - input_str = input_element.value; - document.getElementById("output").removeChild(input_element); - print(input_str); - print("\n"); - resolve(input_str); - } - }); - }); -} - -function tab(space) -{ - var str = ""; - while (space-- > 0) +function tab(space) { + let str = ""; + while (space-- > 0) { str += " "; + } return str; } -var a = [, "S-2","H-2","C-2","D-2","S-3","H-3","C-3","D-3", - "S-4","H-4","C-4","D-4","S-5","H-5","C-5","D-5", - "S-6","H-6","C-6","D-6","S-7","H-7","C-7","D-7", - "S-8","H-8","C-8","D-8","S-9","H-9","C-9","D-9", - "S-10","H-10","C-10","D-10","S-J","H-J","C-J","D-J", - "S-Q","H-Q","C-Q","D-Q","S-K","H-K","C-K","D-K", - "S-A","H-A","C-A","D-A"]; +function input() { + return new Promise(function (resolve) { + const input_element = document.createElement("INPUT"); -var l = []; + print("? "); + input_element.setAttribute("type", "text"); + input_element.setAttribute("length", "50"); + document.getElementById("output").appendChild(input_element); + input_element.focus(); + input_element.addEventListener("keydown", function (event) { + if (event.keyCode == 13) { + const input_str = input_element.value; + document.getElementById("output").removeChild(input_element); + print(input_str); + print("\n"); + resolve(input_str); + } + }); + }); +} -// Main control section -async function main() -{ +async function askYesOrNo(question) { + while (1) { + print(question); + const str = await input(); + if (str == "YES") { + return true; + } + else if (str == "NO") { + return false; + } + else { + print("YES OR NO, PLEASE. "); + } + } +} + +async function askAboutInstructions() { + const playerWantsInstructions = await askYesOrNo("DO YOU WANT DIRECTIONS"); + if (playerWantsInstructions) { + print("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD\n"); + print("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO\n"); + print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n"); + } + print("\n"); + print("\n"); +} + +function createGameDeck(cards, gameSize) { + const deck = []; + const deckSize = cards.length; + for (let j = 0; j < gameSize; j++) { + let card; + + // Compute a new card index until we find one that isn't already in the new deck + do { + card = Math.floor(deckSize * Math.random()); + } while (deck.includes(card)); + deck[j] = card + } + return deck; +} + +function computeCardValue(cardIndex) { + return Math.floor(cardIndex / 4); +} + +function printGameOver(playerScore, computerScore) { + print("\n"); + print("\n"); + print(`WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: ${playerScore} THE COMPUTER: ${computerScore}\n`); + print("\n"); +} + +function printTitle() { print(tab(33) + "WAR\n"); print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); print("\n"); @@ -62,72 +97,65 @@ async function main() print("\n"); print("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#\n"); print("AS S-7 FOR SPADE 7. "); - while (1) { - print("DO YOU WANT DIRECTIONS"); - str = await input(); - if (str == "YES") { - print("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD\n"); - print("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO\n"); - print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n"); - break; - } - if (str == "NO") - break; - print("YES OR NO, PLEASE. "); - } - print("\n"); +} + +function printCards(playerCard, computerCard) { print("\n"); + print(`YOU: ${playerCard}\tCOMPUTER: ${computerCard}\n`); +} + +const cards = [ + "S-2", "H-2", "C-2", "D-2", + "S-3", "H-3", "C-3", "D-3", + "S-4", "H-4", "C-4", "D-4", + "S-5", "H-5", "C-5", "D-5", + "S-6", "H-6", "C-6", "D-6", + "S-7", "H-7", "C-7", "D-7", + "S-8", "H-8", "C-8", "D-8", + "S-9", "H-9", "C-9", "D-9", + "S-10", "H-10", "C-10", "D-10", + "S-J", "H-J", "C-J", "D-J", + "S-Q", "H-Q", "C-Q", "D-Q", + "S-K", "H-K", "C-K", "D-K", + "S-A", "H-A", "C-A", "D-A" +]; + +// Main control section +async function main() { + printTitle(); + await askAboutInstructions(); - a1 = 0; - b1 = 0; - p = 0; + let computerScore = 0; + let playerScore = 0; // Generate a random deck - for (j = 1; j <= 52; j++) { - do { - l[j] = Math.floor(52 * Math.random()) + 1; - for (k = 1; k < j; k++) { - if (l[k] == l[j]) // Already in deck? - break; - } - } while (j != 1 && k < j) ; - } - l[j] = 0; // Mark the end of the deck + const gameSize = 4; + const deck = createGameDeck(cards, gameSize); + let shouldContinuePlaying = true; - while (1) { - m1 = l[++p]; // Take a card - m2 = l[++p]; // Take a card - print("\n"); - print("YOU: " + a[m1] + "\tCOMPUTER: " + a[m2] + "\n"); - n1 = Math.floor((m1 - 0.5) / 4); - n2 = Math.floor((m2 - 0.5) / 4); - if (n1 < n2) { - a1++; - print("THE COMPUTER WINS!!! YOU HAVE " + b1 + " AND THE COMPUTER HAS " + a1 + "\n"); - } else if (n1 > n2) { - b1++; - print("YOU WIN. YOU HAVE " + b1 + " AND THE COMPUTER HAS " + a1 + "\n"); + while (deck.length > 0 && shouldContinuePlaying) { + const m1 = deck.shift(); // Take a card + const m2 = deck.shift(); // Take a card + printCards(cards[m1], cards[m2]); + + const playerCardValue = computeCardValue(m1); + const computerCardValue = computeCardValue(m2); + if (playerCardValue < computerCardValue) { + computerScore++; + print("THE COMPUTER WINS!!! YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n"); + } else if (playerCardValue > computerCardValue) { + playerScore++; + print("YOU WIN. YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n"); } else { print("TIE. NO SCORE CHANGE.\n"); } - if (l[p + 1] == 0) { - print("\n"); - print("\n"); - print("WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: " + b1 + " THE COMPUTER: " + a1 + "\n"); - print("\n"); - break; + + if (deck.length === 0) { + printGameOver(playerScore, computerScore); } - while (1) { - print("DO YOU WANT TO CONTINUE"); - str = await input(); - if (str == "YES") - break; - if (str == "NO") - break; - print("YES OR NO, PLEASE. "); + else { + shouldContinuePlaying = await askYesOrNo("DO YOU WANT TO CONTINUE"); } - if (str == "NO") - break; } print("THANKS FOR PLAYING. IT WAS FUN.\n"); print("\n"); From fe6ff50d2f5d18c32dc71548b2b969de67e3ada8 Mon Sep 17 00:00:00 2001 From: "christopher.millward" Date: Tue, 4 Jan 2022 10:44:48 -0500 Subject: [PATCH 24/26] Updated documentation and used push() --- 94_War/javascript/war.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/94_War/javascript/war.js b/94_War/javascript/war.js index bdc935bc..965294a6 100644 --- a/94_War/javascript/war.js +++ b/94_War/javascript/war.js @@ -73,7 +73,7 @@ function createGameDeck(cards, gameSize) { do { card = Math.floor(deckSize * Math.random()); } while (deck.includes(card)); - deck[j] = card + deck.push(card); } return deck; } @@ -129,7 +129,7 @@ async function main() { let playerScore = 0; // Generate a random deck - const gameSize = 4; + const gameSize = cards.length; // Number of cards to shuffle into the game deck. Can be <= cards.length. const deck = createGameDeck(cards, gameSize); let shouldContinuePlaying = true; From a6957caed0aba39c52d4d2e520aa7abf9926fdea Mon Sep 17 00:00:00 2001 From: "christopher.millward" Date: Tue, 4 Jan 2022 10:46:44 -0500 Subject: [PATCH 25/26] Removed last of the single-letter variables --- 94_War/javascript/war.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/94_War/javascript/war.js b/94_War/javascript/war.js index 965294a6..ededa1ed 100644 --- a/94_War/javascript/war.js +++ b/94_War/javascript/war.js @@ -134,12 +134,12 @@ async function main() { let shouldContinuePlaying = true; while (deck.length > 0 && shouldContinuePlaying) { - const m1 = deck.shift(); // Take a card - const m2 = deck.shift(); // Take a card - printCards(cards[m1], cards[m2]); + const playerCard = deck.shift(); // Take a card + const computerCard = deck.shift(); // Take a card + printCards(cards[playerCard], cards[computerCard]); - const playerCardValue = computeCardValue(m1); - const computerCardValue = computeCardValue(m2); + const playerCardValue = computeCardValue(playerCard); + const computerCardValue = computeCardValue(computerCard); if (playerCardValue < computerCardValue) { computerScore++; print("THE COMPUTER WINS!!! YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n"); From 1b81994c4a00ffc2b60e5d70813482bbd557fa47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Tue, 4 Jan 2022 17:59:28 +0000 Subject: [PATCH 26/26] Add Perl version of 25_Chief --- 25_Chief/perl/chief.pl | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 25_Chief/perl/chief.pl diff --git a/25_Chief/perl/chief.pl b/25_Chief/perl/chief.pl new file mode 100644 index 00000000..d3462514 --- /dev/null +++ b/25_Chief/perl/chief.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +print ' ' x 30 . "CHIEF\n"; +print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; +print "\n\n\n"; + +print "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n"; +print "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR?\n"; + +chomp( my $A = uc ); +print "SHUT UP, PALE FACE WITH WISE TONGUE.\n" unless ( $A eq 'YES' ); + +print " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n"; +print "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n"; +print " WHAT DO YOU HAVE?\n"; + +chomp( my $B = ); +my $C = ( $B + 1 - 5 ) * 5 / 8 * 5 - 3; + +print "I BET YOUR NUMBER WAS $C. AM I RIGHT?\n"; + +chomp( my $D = uc ); +if ( $D eq 'YES' ) { + print "BYE!!!\n"; + exit; +} + +print "WHAT WAS YOUR ORIGINAL NUMBER?\n"; + +chomp( my $K = ); +my $F = $K + 3; +my $G = $F / 5; +my $H = $G * 8; +my $I = $H / 5 + 5; +my $J = $I - 1; + +print "SO YOU THINK YOU'RE SO SMART, EH?\n"; +print "NOW WATCH.\n"; +print "$K PLUS 3 EQUALS $F. THIS DIVIDED BY 5 EQUALS $G;\n"; +print "THIS TIMES 8 EQUALS $H. IF WE DIVIDE BY 5 AND ADD 5,\n"; +print "WE GET $I , WHICH, MINUS 1, EQUALS $J.\n"; +print "NOW DO YOU BELIEVE ME?\n"; + +chomp( my $Z = uc ); +if ( $Z eq 'YES' ) { + print "BYE!!!\n"; + exit; +} + +print "YOU HAVE MADE ME MAD!!!\n"; +print "THERE MUST BE A GREAT LIGHTNING BOLT!\n\n\n"; + +for my $i ( reverse 22 .. 30 ) { + print ' ' x $i . "X X\n"; +} +print ' ' x 21 . "X XXX\n"; +print ' ' x 20 . "X X\n"; +print ' ' x 19 . "XX X\n"; +for my $i ( reverse 13 .. 20 ) { + print ' ' x $i . "X X\n"; +} +print ' ' x 12 . "XX\n"; +print ' ' x 11 . "X\n"; +print ' ' x 10 . "*\n"; +print "\n#########################\n\n"; +print "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n";