about summary refs log tree commit diff stats
path: root/forth/guesser.fth
blob: 01d84a0857c62d9ab9333dd19593aaa8f728233f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
\ --- Number Guessing Game for pForth ---

\ First, we need some helper words for reliable numeric input.
\ These are based on the pForth tutorial.
( -- $addr )
: INPUT$ ( A word to get a line of text from the user )
    PAD 1+      ( addr --- , leave room on the scratchpad for a byte count )
    80 ACCEPT   ( addr maxbytes -- numbytes , get up to 80 chars )
    PAD C!      ( numbytes --- , store the count in the first byte )
    PAD         ( -- $addr , leave the address of the counted string )
;

( -- n true | false )
: INPUT# ( Read a line and convert it to a number )
    INPUT$      ( -- $addr )
    NUMBER?     ( $addr -- d_num true | false , convert string to double )
    IF
        SWAP DROP TRUE  ( d_num true -- n true , drop high part of double )
    ELSE
        FALSE           ( -- false )
    THEN
;

\ --- Game Logic ---

VARIABLE SECRET#  \ A place to store the secret number.

( -- )
: INIT-SECRET# ( Generate and store a random number )
    501 CHOOSE  ( -- rand , CHOOSE gives a number from 0 to n-1 )
    SECRET# !   ( rand -- , store it in our variable )
;

( -- n )
: GET-GUESS ( Loop until the user enters a valid number )
    BEGIN
        CR ." Your guess (0-500)? "
        INPUT#      ( -- n true | false )
    UNTIL           ( loops until flag is true )
;

( guess -- correct? )
: CHECK-GUESS ( Compares guess to the secret number, gives a hint )
    SECRET# @       ( guess -- guess secret# )
    2DUP =          ( guess secret# -- guess secret# flag )
    IF              ( guess is equal to secret# )
        ." You got it!" CR
        2DROP TRUE  \ --> Make sure this 2DROP is here.
    ELSE            ( guess is not equal )
        2DUP <      ( guess secret# -- guess secret# flag )
        IF
            ." Too low!" CR
            2DROP FALSE \ --> And this one.
        ELSE
            ." Too high!" CR
            2DROP FALSE \ --> And this one.
        THEN
    THEN
;

( -- )
: SEED-BY-WAITING ( Uses user's reaction time to seed the PRNG )
    CR ." Press any key to begin..."
    BEGIN
        501 CHOOSE DROP  \ "Burn" a random number from the sequence
        ?TERMINAL        \ Check if a key has been pressed
    UNTIL
    CR
;

( -- )
: GUESSING-GAME ( The main word to run the game )
    SEED-BY-WAITING \ Call our new interactive seeder
    INIT-SECRET#
    CR ." I'm thinking of a number between 0 and 500."
    CR ." You have 5 chances to guess it."

    FALSE           ( -- user-won? , place a 'false' flag on the stack )

    5 0 DO          ( loop 5 times, from 0 to 4 )
        CR 5 I - . ." guesses left."
        GET-GUESS
        CHECK-GUESS ( guess -- correct? )
        IF          ( a correct guess was made )
            DROP TRUE ( replace the 'user-won?' flag with 'true' )
            LEAVE     ( and exit the loop immediately )
        THEN
    LOOP

    ( The 'user-won?' flag is now on top of the stack )
    IF
        CR ." Congratulations!" CR
    ELSE
        CR ." Sorry, you ran out of guesses."
        CR ." The number was " SECRET# @ . CR
    THEN
;

GUESSING-GAME   \ This line executes the game word we just defined.
BYE             \ This tells pForth to exit when the game is over.