diff --git a/01 Acey Ducey/raku/README.md b/01 Acey Ducey/raku/README.md new file mode 100644 index 00000000..47ef380b --- /dev/null +++ b/01 Acey Ducey/raku/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Raku](https://raku.org/) diff --git a/01 Acey Ducey/raku/aceyducey.raku b/01 Acey Ducey/raku/aceyducey.raku new file mode 100644 index 00000000..f97599ae --- /dev/null +++ b/01 Acey Ducey/raku/aceyducey.raku @@ -0,0 +1,188 @@ +#!/usr/bin/env raku +use v6.d; + +=begin comment +This is a complete rewrite from scratch +=end comment + +# Note that this is a special sub that can be called automatically +sub USAGE () { + print q:to + Two cards are dealt face-up. + You then bet how much money you want to risk. + + If the next card falls between those two cards you gain the amount you bet. + If it is outside of those cards, you lose that amount + If it matches one of the cards, you lose double. + + If the first two cards are the same value, + you guess whether the next card will be higher or lower, and bet on that. + If all three cards are the same, you lose triple your bet. + END +} + +constant $Card-Back = "\c[PLAYING CARD BACK]"; + +class Card { + # for viewing + has Str $.Str is required; + + # for comparison + has Int $.Int is required; + method Numeric () { $!Int } + method Real () { $!Int } +} + +class Deck { + # Internal use: + # convert to a Card object + sub card (Pair (:key($Str), :value($Int))) { + Card.new(:$Str, :$Int) + } + + # Internal use: + # a base deck of Cards that needs to be shuffled + constant @Base-Deck := flat( + ('🂡' .. '🂮' Z=> flat 14, 2 .. 13), + ('🂱' .. '🂾' Z=> flat 14, 2 .. 13), + ('🃁' .. '🃎' Z=> flat 14, 2 .. 13), + ('🃑' .. '🃞' Z=> flat 14, 2 .. 13) + ).map(&card).List; + + # ----------------------------------- + # beginning of attributes and methods + # ----------------------------------- + + # create private attribute $!bag + # It is a SetHash created from @Base-Deck + # we use its .grab(|) method as .deal(|) + has SetHash $!bag handles ('deal' => 'grab') .= new(@Base-Deck); + + # note that you should not reshuffle if you are currently holding any cards + method reshuffle () { + $!bag .= new(@Base-Deck); + } + + method maybe-reshuffle (UInt $to-take = 0) { + # If there isn't enough cards, it always reshuffles + # otherwise there is a 1% chance that it will reshuffle + if $to-take > +$!bag || rand < 0.1e0 { + put 'Shuffling the cards'; + self.reshuffle; + } + } +} + + + +sub MAIN () { + my Deck $deck .= new; + + USAGE(); + print "\n\n"; + + Restart: + repeat { + my Int $*pot = 100; + + Main-Loop: + loop { + # make sure that there is money left to gamble + last if $*pot <= 0; + # shuffle if there isn't enough cards in the deck + # or randomly otherwise + NEXT $deck.maybe-reshuffle(3); + + + # sort numerically + my ($a,$b) = $deck.deal(2).sort(+*); + put "$a $b $Card-Back"; + + + my Order $*more-or-less; + my UInt $bet; + + + # Check the dealt cards + if $a == $b { # numerically equal + $*more-or-less = do given $a { + when $a == 2 { More } + when $a == 14 { Less } + default { more-or-less } + } + } elsif $a + 1 == $b { # consecutive cards + put "There isn't a chance of winning, redealing."; + redo + } + + + $bet = make-a-bet; + if $bet == 0 { + say 'Coward'; + redo; + } + + # deal a new card + my $c = $deck.deal; + put "$a $b $c"; + + # check the new card + { + when $a < $c < $b { + put 'Win. '; + $*pot += $bet + } + when $c == $a | $b { + put 'Lose Double. '; + $*pot -= $bet * 2 + } + default { + put 'Lose. '; + $*pot -= $bet + } + } + + last if $*pot <= 0; + put "You have $*pot"; + } + + } while play-again; +} + +sub more-or-less (--> Order) { + loop { + my $answer = prompt 'Is the next card going to be MORE or LESS than that? '; + given fc $answer { + when 'less' | '<' | 'before' { return Order::Less } + when 'more' | '>' | 'after' { return Order::More } + } + } +} + +sub make-a-bet () { + put "You currently have $*pot"; + my $message = do given $*more-or-less { + when Less { 'How much money are you willing to bet that it is less? ' } + when More { 'How much money are you willing to bet that it is more? ' } + default { 'How much money are you willing to bet? ' } + }; + + loop { + # redo the loop if there is an error + CATCH { default {} } + + my $answer = prompt $message; + if $answer eq '*' { + return $*pot + } elsif $answer > $*pot { + put "You only have $*pot"; + } else { + return $answer; + } + } +} + +sub play-again (--> Bool) { + my $answer = prompt "\nYou ran out of money.\nDo you want to play again? "; + so $answer.fc eq any < y yes yeah sure ok > +} \ No newline at end of file diff --git a/01 Acey Ducey/raku/aceyducey_direct.raku b/01 Acey Ducey/raku/aceyducey_direct.raku new file mode 100644 index 00000000..53b5200d --- /dev/null +++ b/01 Acey Ducey/raku/aceyducey_direct.raku @@ -0,0 +1,131 @@ +#!/usr/bin/env raku +use v6.d; + +=begin comment +This is intended to be a close translation of the original. + +While Raku is "supposed" to have a `goto`, no one has ever bothered to implemented it. +So we use `repeat {...} while False;` and `redo` to emulate them. +Could have instead used `loop {...}` and `last` instead, +but that wouldn't line up with the `goto` statements of the original. + +I also took the liberty of vastly simplifying the dealing of the cards +in lines 260..630 and 730..890 +I might change them to be more similar to the original later +=end comment + +## the following lines replace lines 10..80 +put qq:to; + { ' ' x 26 }ACEY DUCEY CARD GAME + { ' ' x 15 }CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + + ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER + THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP + YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING + ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE + A VALUE BETWEEN THE FIRST TWO. + IF YOU DO NOT WANT TO BET, INPUT A 0 + END + +constant @CARDS = flat 2..10, ; + +## used to sort the cards in the same order as they +## appear in @CARDS +constant %CARD-INDEX = @CARDS[]:p .invert; +## @CARDS[]:p eqv (0 => 2, 1 => 3 ... 8 => 10, 9 => "Jack", 10 => "Queen", 11 => "King", 12 => "Ace") +## .invert() inverts the key and value +# %CARD-INDEX = ( +# 2 => 0, +# ... +# 10 => 8, +# Jack => 9, +# Queen => 10, +# King => 11, +# Ace => 12, +# ) + +START: repeat { + # my $N = 100; # line 100 + my $Q = 100; # line 110 + + my ($A,$B); + my $M; + MAIN-LOOP: repeat { + put "YOU NOW HAVE $Q DOLLARS."; # line 120 + put(); # line 130 + + DEAL-HAND: repeat { + ## The following 2 lines replace lines 260..630 + # the two cards can't match, so use pick() instead of roll() + # we also want them sorted in the same order as they are in @CARDS + my @hand = @CARDS.pick(2).sort({ %CARD-INDEX{$_} }); + ($A,$B) = @hand; + put "HERE ARE YOUR NEXT TWO CARDS: ", @hand; + + put(); # line 640 + # put(); # line 650 + + BET: repeat { + $M = prompt "WHAT IS YOUR BET "; # line 660 + given $M { + when 0 { # line 670 + put "CHICKEN!!"; # line 675 + put(); # line 676 + redo DEAL-HAND; # line 677 + } + when $M <= $Q {} # line 680 + when $M > $Q { # line 680 also + put "SORRY, MY FRIEND, BUT YOU BET TOO MUCH."; # line 690 + put "YOU HAVE ONLY $Q DOLLARS TO BET."; # line 700 + redo BET; # line 710 + } + + ## This is new. + ## It is used to handle errors like non-numeric bets + ## (It's also why line 680 is split among two lines above) + default { + put "PLEASE GIVE A NUMBER BETWEEN 0 AND $Q"; + redo BET; + } + } + } while False; # BET: + } while False; # DEAL-HAND: + + ## The following two lines replace lines 730..890 + my $C = @CARDS.pick; + put $C; + + ## This replaces lines 910..970 + # note also that lines 210,220,240,250 were moved here + my ($a,$b,$c) = %CARD-INDEX{$A,$B,$C}; + if $a < $c < $b { # lines 930,970 + put "YOU WIN!!!"; # line 950 + $Q += $M; # line 210 + redo MAIN-LOOP; # line 220 + } + put "SORRY, YOU LOSE"; # line 970 + + # still have money left? + if $M < $Q { # line 980 + $Q -= $M; # line 240 + redo MAIN-LOOP; # line 250 + } + } while False; # MAIN-LOOP: + + # out of money + put(); # line 990 + put(); # line 1000 + put "SORRY, FRIEND, BUT YOU BLEW YOUR WAD."; # line 1010 + put(); put(); # line 1015 + + my $Again = prompt "TRY AGAIN (YES OR NO)"; # line 1020 + put(); put(); # line 1025 + + if $Again.uc eq "YES" { # line 1030 + redo START; # line 1030 + } + + put "O.K., HOPE YOU HAD FUN!"; #line 1040 + # exit; # line 1050 +} while False; # START: