added numberjack blackjack game

This commit is contained in:
Luxferre
2025-04-30 09:31:12 +03:00
parent 8122ca07dd
commit acd18af182
3 changed files with 287 additions and 3 deletions
+4 -3
View File
@@ -273,12 +273,13 @@ namely:
* [A simple 10-character echo test](examples/echo.mu8a) (requires both I/O
port 1 and port 2 support),
* [Bulls and Cows game](examples/moo.mu8a),
* [Lunar Lander game](examples/lunar.mu8a).
* [Lunar Lander game](examples/lunar.mu8a),
* [NumberJack](examples/numjack.mu8a) port of a Blackjack game, utilizing some
advanced techniques (see the comments in the beginning on how to play it).
You can assemble them using any of the reference assemblers provided within the
repository, or even by hand (by numbering lines, resolving the labels and
replacing mnemonics with corresponding opcodes). The "Hellorld!" example only
works on the platforms supporting I/O port 1 (character output).
replacing mnemonics with corresponding opcodes).
If you just want to test an implementation, assembled MU8 machine code files
(in the plaintext format) are stored in the `examples/assembled` subdirectory.
+128
View File
@@ -0,0 +1,128 @@
1 5 1 0 50
2 5 10 0 51
3 5 100 0 52
4 5 13 0 53
5 5 16 0 54
6 5 21 0 55
7 5 12 0 56
8 5 1000 0 60
9 5 2 0 57
10 5 36 0 58
11 5 101 0 80
12 5 110 0 81
13 1 6 0 40
14 15 50 53 1
15 9 1 51 2
16 10 2 0 2
17 8 50 2 2
18 7 0 1 2
19 1 13 0 99
20 5 0 0 2
21 5 0 0 8
22 10 1 51 4
23 9 1 51 1
24 10 1 0 1
25 1 1 4 27
26 7 51 50 4
27 7 4 50 2
28 8 4 50 5
29 1 5 5 31
30 7 50 50 8
31 8 1 50 5
32 1 1 5 22
33 6 1 2 3
34 8 8 50 5
35 1 5 5 39
36 8 2 56 5
37 1 3 5 39
38 7 51 50 3
39 1 13 0 99
40 3 58 58 1
41 3 60 60 0
42 1 0 60 128
43 4 61 61 0
44 8 60 61 60
45 1 2 60 128
46 6 0 48 99
47 1 6 0 14
48 7 52 51 2
49 6 1 2 62
50 6 0 52 99
51 1 6 0 14
52 7 2 50 62
53 6 1 62 1
54 6 0 56 99
55 1 6 0 20
56 8 3 55 5
57 1 0 5 40
58 6 0 60 99
59 1 6 0 14
60 7 52 51 2
61 6 1 2 63
62 6 0 64 99
63 1 6 0 14
64 7 2 50 63
65 9 62 51 5
66 10 5 0 5
67 3 5 5 0
68 5 0 0 64
69 5 0 0 9
70 3 63 63 0
71 4 6 6 0
72 1 0 6 87
73 8 6 50 5
74 1 0 5 82
75 8 5 50 5
76 1 0 5 78
77 1 6 0 89
78 1 5 64 89
79 8 60 61 60
80 7 61 50 61
81 5 1 0 9
82 6 0 84 99
83 1 6 0 14
84 7 2 51 63
85 7 50 50 64
86 1 6 0 89
87 5 1 0 9
88 7 50 50 64
89 6 1 63 1
90 6 0 92 99
91 1 6 0 20
92 8 2 55 5
93 1 4 5 96
94 3 63 63 0
95 1 6 0 40
96 1 0 9 70
97 6 1 3 72
98 5 0 0 9
99 6 1 62 1
100 6 0 102 99
101 1 6 0 20
102 8 2 55 5
103 8 3 54 6
104 1 1 5 107
105 1 1 6 107
106 1 6 0 108
107 1 6 0 112
108 6 0 110 99
109 1 6 0 14
110 7 2 51 62
111 1 0 9 99
112 3 62 63 0
113 8 63 80 1
114 1 0 1 123
115 8 63 81 1
116 1 0 1 123
117 8 3 72 1
118 1 0 1 126
119 1 2 1 125
120 8 2 55 1
121 1 1 1 125
122 1 6 0 40
123 9 61 57 1
124 7 1 50 60
125 7 61 50 60
126 7 61 50 60
127 1 6 0 40
128 0 0 0 0
+155
View File
@@ -0,0 +1,155 @@
; NumberJack: a number-only blackjack game in MU8A for the mu808 VM
; How to play:
; * you start with a $1000 balance
; * on each round, enter your bet
; (the game will quit if the bet is above your balance)
; * the first card of the dealer's hand will be shown
; (hands are shown with a leading 1 that you must ignore)
; (card values are: 1 is ace, 2 to 9 are "as is", 0 is 10 to K)
; * on the first turn, select 0 (stand), 1 (hit) or 2 (double)
; * on each next turn, select 0 (stand) or 1 (hit)
; * as a result of the round, the dealer's final hand will be shown
; first and then yours
; * the dealer must draw on 16 and stand on any 17
; * player's blackjack pays 3 to 2
; Created by Luxferre in 2025, released into public domain
; constants section
set 1 0 50 ; set constant 1 to loc 50
set 10 0 51 ; set constant 10 to loc 51
set 100 0 52 ; set constant 100 to loc 52
set 13 0 53 ; upper RNG limit to loc 53
set 16 0 54 ; dealer draw limit to loc 54
set 21 0 55 ; 21 score limit to loc 55
set 12 0 56 ; 12 score limit to loc 56
set 1000 0 60 ; set $1000 initial balance
set 2 0 57 ; set constant 2 to loc 57
set 36 0 58 ; set constant 36 to loc 58
set 101 0 80 ; set the first blackjack condition to loc 80
set 110 0 81 ; set the second blackjack condition to loc 81
juc :main ; jump to the main code
; subroutines section (return address is in loc 99)
; random card pulling procedure (result is in loc 2, from 0 to 9)
:gcard rnd 50 53 1 ; set loc 1 to random integer between 1 and 13
div 1 51 2 ; divide it by 10 into loc 2
mdf 2 0 2 ; get the floor part of the result into the same loc 2
sub 50 2 2 ; subtract it from 1
fma 0 1 2 ; multiply by the random choice itself, result is in loc 2
iuc 99 ; jump to the return address in loc 99
; score evaluation procedure
; param is in loc 1, resulting scores are in locs 2 and 3
:score set 0 0 2 ; set score1 to 0
set 0 0 8 ; set aces to 0
:slp mdf 1 51 4 ; get the current digit n of val
div 1 51 1 ; divide val by 10
mdf 1 0 1 ; get the floor part into the same loc
jgt 4 :scn ; skip if n > 0
fma 51 50 4 ; add 10 to n
:scn fma 4 50 2 ; add n to score1 at loc 2
sub 4 50 5 ; subtract 1 from n to loc 5
jne 5 :sc2 ; skip if n != 1
fma 50 50 8 ; increment aces count at loc 8
:sc2 sub 1 50 5 ; subtract 1 from val to loc 5
jgt 5 :slp ; go to loop start if val > 1
cpy 1 2 3 ; assign score2 (at loc 3) = score1 (at loc 2)
sub 8 50 5 ; subtract 1 from aces (loc 8)
jne 5 :send ; go to procedure end if aces != 1
sub 2 56 5 ; subtract 12 from the score1 value
jge 5 :send ; go to procedure end if score1 >= 12
fma 51 50 3 ; add 10 to score2 if there was exactly one non-busting ace
:send iuc 99 ; jump to the return address in loc 99
; main code section
; variables:
; 60 - balance, 61 - bet, 62 - dealer's hand, 63 - player's hand, 64 - round
:main out 58 58 1 ; output the dollar sign (if supported)
out 60 60 0 ; output the current balance
jeq 60 :end ; game over if it's zero
inp 61 61 0 ; input your bet
sub 60 61 60 ; subtract the bet from the balance
jlt 60 :end ; game over if over balance
cpy 0 :r1 99 ; set the return address
juc :gcard ; call the card generation procedure
:r1 fma 52 51 2 ; loc 2 <- 100 + 10 * card in loc 2
cpy 1 2 62 ; copy the result into loc 62
cpy 0 :r2 99 ; set the return address
juc :gcard ; call the card generation procedure again (in loc 2)
:r2 fma 2 50 62 ; add this result into loc 62 (dealer's hand)
cpy 1 62 1 ; copy the dealer's hand as the proc parameter
cpy 0 :r3 99 ; set the return address
juc :score ; call the scoring procedure
:r3 sub 3 55 5 ; we only have to compare score 2 to 21
jeq 5 :main ; start the loop over if we have dealer's blackjack
cpy 0 :r4 99 ; set the return address
juc :gcard ; call the card generation procedure
:r4 fma 52 51 2 ; loc 2 <- 100 + 10 * card in loc 2
cpy 1 2 63 ; copy the result into loc 63 (player's hand)
cpy 0 :r5 99 ; set the return address
juc :gcard ; call the card generation procedure again (in loc 2)
:r5 fma 2 50 63 ; add this result into loc 63 (player's hand)
div 62 51 5 ; get the start of the dealer's hand by dividing it by 10...
mdf 5 0 5 ; and getting the floor part
out 5 5 0 ; display the start of the dealer's hand
set 0 0 64 ; set the round index to 0
set 0 0 9 ; set the stand flag to 0
:rnl out 63 63 0 ; start of the inner player loop, display the player's hand
inp 6 6 0 ; input the action into loc 6
jeq 6 :std ; jump to stand action if 0
sub 6 50 5 ; check if 1
jeq 5 :hit ; jump to hit action if 1
sub 5 50 5 ; check if 2
jeq 5 :dbl ; jump to double action if 2
juc :skp ; jump to skip otherwise
:dbl jne 64 :skp ; skip double if not the first round
sub 60 61 60 ; subtract more balance
fma 61 50 61 ; double the bet
set 1 0 9 ; set the stand flag, proceed to the hit section next
:hit cpy 0 :r6 99 ; set the return address
juc :gcard ; call the card generation procedure
:r6 fma 2 51 63 ; loc 63 <- loc 63 * 10 + card in loc 2
fma 50 50 64 ; increment the round index
juc :skp ; jump to skip afterwards
:std set 1 0 9 ; set the stand flag
fma 50 50 64 ; increment the round index
:skp cpy 1 63 1 ; copy the current player's hand to loc 1 as a parameter
cpy 0 :r7 99 ; set the return address
juc :score ; call the scoring procedure
:r7 sub 2 55 5 ; we only have to compare score 1 to 21
jle 5 :nob ; reloop to beginning if the player is bust
out 63 63 0 ; output the player's hand
juc :main ; reloop
:nob jeq 9 :rnl ; repeat the inner player loop if the stand flag is 0
cpy 1 3 72 ; copy the player score 2 to loc 72
set 0 0 9 ; set the stand flag to 0
:rnd cpy 1 62 1 ; start the inner dealer's loop, set the scoring procedure param
cpy 0 :r9 99 ; set the return address
juc :score ; call the scoring procedure
:r9 sub 2 55 5 ; we need to compare score 1 to 21 => loc 5
sub 3 54 6 ; we need to compare score 2 to 16 => loc 6
jgt 5 :s3 ; go to stand if score1 > 21
jgt 6 :s3 ; go to stand if score2 > 16
juc :s4 ; skip if neither
:s3 juc :s5 ; jump outside to stand
:s4 cpy 0 :r8 99 ; set the return address
juc :gcard ; call the card generation procedure
:r8 fma 2 51 62 ; loc 62 <- loc 62 * 10 + card in loc 2
jeq 9 :rnd ; repeat the inner dealer loop if the stand flag is 0
:s5 out 62 63 0 ; output both hands (dealer's first)
sub 63 80 1 ; compare player hand with 101
jeq 1 :bjk ; jump to the blackjack condition if equal
sub 63 81 1 ; compare player hand with 110
jeq 1 :bjk ; jump to the blackjack condition if equal
sub 3 72 1 ; compare dealer score 2 and player score 2
jeq 1 :push ; push condition
jlt 1 :win ; player win condition
sub 2 55 1 ; compare dealer score 1 with 21
jgt 1 :win ; player win condition
juc :main ; reloop to beginning
:bjk div 61 57 1 ; halve the bet
fma 1 50 60 ; add half the bet (for blackjack calc)
:win fma 61 50 60 ; add bet first time
:push fma 61 50 60 ; add bet second time
juc :main ; reloop to beginning
:end nop 0 0 0 ; just a nop label for the program end