|
1 | 1 | # RUN: rlc %s -o %t -i %stdlib
|
2 | 2 |
|
3 | 3 | import serialization.print
|
| 4 | +import range |
4 | 5 | import collections.vector
|
5 | 6 | import machine_learning
|
6 | 7 | import action
|
7 | 8 |
|
8 | 9 | # Constants for the game
|
9 |
| -const kDefaultRows = 10 |
10 |
| -const kDefaultCols = 5 |
11 |
| -const kNumCells = 50 # kDefaultRows * kDefaultCols |
| 10 | +const NUM_ROWS = 11 |
| 11 | +const NUM_COLUMS = 5 |
12 | 12 |
|
13 |
| -# Cell states |
14 |
| -enum CellState: |
15 |
| - Empty |
16 |
| - Paddle |
17 |
| - Ball |
| 13 | +enum Direction: |
| 14 | + Left |
| 15 | + None |
| 16 | + Right |
18 | 17 |
|
19 |
| - fun equal(CellState other) -> Bool: |
| 18 | + fun equal(Direction other) -> Bool: |
20 | 19 | return self.value == other.value
|
21 | 20 |
|
22 |
| - fun not_equal(CellState other) -> Bool: |
| 21 | + fun not_equal(Direction other) -> Bool: |
23 | 22 | return !(self.value == other.value)
|
24 | 23 |
|
25 |
| -# Board representation |
26 |
| -cls Board: |
27 |
| - BInt<2,99> num_rows |
28 |
| - BInt<2,99> num_cols |
29 |
| - BInt<0,98> ball_row |
30 |
| - BInt<0,98> ball_col |
31 |
| - BInt<0,98> paddle_col |
32 |
| - |
33 |
| - fun init(): |
34 |
| - self.num_rows.value = kDefaultRows |
35 |
| - self.num_cols.value = kDefaultCols |
36 |
| - self.paddle_col.value = kDefaultCols / 2 |
37 |
| - |
38 |
| - fun init(Int rows, Int cols): |
39 |
| - self.num_rows.value = rows |
40 |
| - self.num_cols.value = cols |
41 |
| - self.paddle_col.value = cols / 2 |
42 |
| - |
43 |
| - # Get cell state at specific row and column |
44 |
| - fun cell_at(Int row, Int col) -> CellState: |
45 |
| - if row == self.num_rows.value - 1 and col == self.paddle_col.value: |
46 |
| - return CellState::Paddle |
47 |
| - else if row == self.ball_row.value and col == self.ball_col.value: |
48 |
| - return CellState::Ball |
49 |
| - return CellState::Empty |
50 |
| - |
51 |
| - # Check if the game is terminal |
52 |
| - fun is_terminal() -> Bool: |
53 |
| - return self.ball_row.value >= self.num_rows.value - 1 |
54 | 24 |
|
55 |
| - # Generate string representation of the board |
56 |
| - fun to_string() -> String: |
57 |
| - let result = ""s |
58 |
| - let row = 0 |
59 |
| - while row < self.num_rows.value: |
60 |
| - let col = 0 |
61 |
| - while col < self.num_cols.value: |
62 |
| - if self.cell_at(row, col) == CellState::Empty: |
63 |
| - result = result + "."s |
64 |
| - else if self.cell_at(row, col) == CellState::Paddle: |
65 |
| - result = result + "x"s |
66 |
| - else: |
67 |
| - result = result + "o"s |
68 |
| - col = col + 1 |
69 |
| - result = result + "\n"s |
70 |
| - row = row + 1 |
71 |
| - return result |
| 25 | +using Column = BInt<0, NUM_COLUMS> |
| 26 | +using Row = BInt<0, NUM_ROWS> |
72 | 27 |
|
73 | 28 | # The main Catch game
|
74 | 29 | @classes
|
75 | 30 | act play() -> Game:
|
76 |
| - frm board : Board |
| 31 | + frm ball_row : Row |
| 32 | + frm ball_col : Column |
| 33 | + frm paddle_col : Column |
77 | 34 |
|
78 | 35 | # Initialization - chance player selects starting ball column
|
79 |
| - act initialize(BInt<0, kDefaultCols> col) { |
80 |
| - col.value >= 0, |
81 |
| - col.value < kDefaultCols |
82 |
| - } |
83 |
| - board.ball_col.value = col.value |
| 36 | + act set_start_location(Column col) |
| 37 | + ball_col.value = col.value |
| 38 | + paddle_col = NUM_COLUMS / 2 |
84 | 39 |
|
85 | 40 | # Game loop - player makes moves until ball reaches bottom
|
86 |
| - while !board.is_terminal(): |
87 |
| - act move(BInt<0, 2> direction) { |
88 |
| - direction >= 0, |
89 |
| - direction < 3 |
90 |
| - } |
91 |
| - |
92 |
| - # Move the ball down one row |
93 |
| - board.ball_row.value = board.ball_row.value + 1 |
| 41 | + while ball_row != NUM_ROWS - 1: |
| 42 | + act move(Direction direction) |
| 43 | + |
| 44 | + ball_row.value = ball_row.value + 1 |
94 | 45 |
|
95 |
| - # Move the paddle based on player's direction |
96 |
| - # 0 = left, 1 = stay, 2 = right |
97 | 46 | let actual_direction = direction.value - 1 # Convert to -1, 0, 1
|
98 |
| - let new_paddle_pos = board.paddle_col.value + actual_direction |
| 47 | + paddle_col = paddle_col.value + actual_direction |
| 48 | + |
| 49 | +# Cell states |
| 50 | +enum CellState: |
| 51 | + Empty |
| 52 | + Paddle |
| 53 | + Ball |
| 54 | + |
| 55 | + fun equal(CellState other) -> Bool: |
| 56 | + return self.value == other.value |
| 57 | + |
| 58 | + fun not_equal(CellState other) -> Bool: |
| 59 | + return !(self.value == other.value) |
| 60 | + |
| 61 | +# Get cell state at specific row and column |
| 62 | +fun cell_at(Game game, Int row, Int col) -> CellState: |
| 63 | + if row == NUM_ROWS - 1 and col == game.paddle_col.value: |
| 64 | + return CellState::Paddle |
| 65 | + else if row == game.ball_row.value and col == game.ball_col.value: |
| 66 | + return CellState::Ball |
| 67 | + return CellState::Empty |
| 68 | + |
99 | 69 |
|
100 |
| - # Ensure paddle stays within bounds |
101 |
| - if new_paddle_pos >= 0 and new_paddle_pos < board.num_cols.value: |
102 |
| - board.paddle_col.value = new_paddle_pos |
103 | 70 |
|
104 | 71 | # Function for machine learning components to display the game state
|
105 | 72 | fun pretty_print(Game game):
|
106 |
| - print(game.board.to_string()) |
107 |
| - |
108 |
| - # Print game status |
109 |
| - if game.is_done(): |
110 |
| - if score(game, 0) == 1.0: |
111 |
| - print("You win! You caught the ball."s) |
112 |
| - else: |
113 |
| - print("You lose! You missed the ball."s) |
114 |
| - else: |
115 |
| - print("Game in progress. Ball at row "s + to_string(game.board.ball_row) + ", column "s + to_string(game.board.ball_col)) |
116 |
| - print("Paddle at column "s + to_string(game.board.paddle_col)) |
| 73 | + # Generate string representation of the board |
| 74 | + let result = ""s |
| 75 | + for row in range(NUM_ROWS): |
| 76 | + for col in range(NUM_COLUMS): |
| 77 | + if cell_at(game, row, col) == CellState::Empty: |
| 78 | + result = result + "."s |
| 79 | + else if cell_at(game, row, col) == CellState::Paddle: |
| 80 | + result = result + "x"s |
| 81 | + else: |
| 82 | + result = result + "o"s |
| 83 | + result = result + "\n"s |
| 84 | + print(result) |
| 85 | + |
117 | 86 |
|
118 | 87 | # Return current player or special value if game is done
|
119 | 88 | fun get_current_player(Game g) -> Int:
|
120 | 89 | if g.is_done():
|
121 | 90 | return -4 # Terminal state
|
| 91 | + let column : Column |
| 92 | + if can g.set_start_location(column): |
| 93 | + return -1 |
122 | 94 | return 0 # Player 0 (the only player)
|
123 | 95 |
|
124 | 96 | # Return score for ML training
|
125 | 97 | fun score(Game g, Int player_id) -> Float:
|
126 | 98 | if !g.is_done():
|
127 | 99 | return 0.0
|
128 |
| - if g.board.paddle_col == g.board.ball_col: |
| 100 | + if g.paddle_col.value == g.ball_col.value: |
129 | 101 | return 1.0
|
130 | 102 | else:
|
131 |
| - return -1.0 |
| 103 | + return 0.0 |
132 | 104 |
|
133 | 105 | # Return number of players (always 1 for this game)
|
134 | 106 | fun get_num_players() -> Int:
|
135 | 107 | return 1
|
136 | 108 |
|
137 |
| -fun fuzz(Vector<Byte> input): |
138 |
| - let state = play() |
139 |
| - let x : AnyGameAction |
140 |
| - let enumeration = enumerate(x) |
141 |
| - let index = 0 |
142 |
| - while index + 8 < input.size() and !state.is_done(): |
143 |
| - let num_action : Int |
144 |
| - from_byte_vector(num_action, input, index) |
145 |
| - if num_action < 0: |
146 |
| - num_action = num_action * -1 |
147 |
| - if num_action < 0: |
148 |
| - num_action = 0 |
149 |
| - |
150 |
| - let executable : Vector<AnyGameAction> |
151 |
| - let i = 0 |
152 |
| - #print("VALIDS") |
153 |
| - while i < enumeration.size(): |
154 |
| - if can apply(enumeration.get(i), state): |
155 |
| - #print(enumeration.get(i)) |
156 |
| - executable.append(enumeration.get(i)) |
157 |
| - i = i + 1 |
158 |
| - #print("ENDVALIDS") |
159 |
| - #if executable.size() == 0: |
160 |
| - #print("zero valid actions") |
161 |
| - #print(state) |
162 |
| - #return |
163 |
| - |
164 |
| - print(executable.get(num_action % executable.size())) |
165 |
| - apply(executable.get(num_action % executable.size()), state) |
0 commit comments