File: trisdemo.lua

package info (click to toggle)
haproxy 3.2.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,924 kB
  • sloc: ansic: 267,927; sh: 3,466; xml: 1,756; python: 1,345; makefile: 1,155; perl: 168; cpp: 21
file content (251 lines) | stat: -rw-r--r-- 8,420 bytes parent folder | download | duplicates (2)
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
-- Example game of falling pieces for HAProxy CLI/Applet
local board_width = 10
local board_height = 20
local game_name = "Lua Tris Demo"

-- Shapes with IDs for color mapping
local pieces = {
    {id = 1, shape = {{1,1,1,1}}},        -- I (Cyan)
    {id = 2, shape = {{1,1},{1,1}}},      -- O (Yellow)
    {id = 3, shape = {{0,1,0},{1,1,1}}},  -- T (Purple)
    {id = 4, shape = {{0,1,1},{1,1,0}}},  -- S (Green)
    {id = 5, shape = {{1,1,0},{0,1,1}}},  -- Z (Red)
    {id = 6, shape = {{1,0,0},{1,1,1}}},  -- J (Blue)
    {id = 7, shape = {{0,0,1},{1,1,1}}}   -- L (Orange)
}

-- ANSI escape codes
local clear_screen = "\27[2J"
local cursor_home = "\27[H"
local cursor_hide = "\27[?25l"
local cursor_show = "\27[?25h"
local reset_color = "\27[0m"

local color_codes = {
    [1] = "\27[1;36m", -- I: Cyan
    [2] = "\27[1;37m", -- O: White
    [3] = "\27[1;35m", -- T: Purple
    [4] = "\27[1;32m", -- S: Green
    [5] = "\27[1;31m", -- Z: Red
    [6] = "\27[1;34m", -- J: Blue
    [7] = "\27[1;33m"  -- L: Yellow
}

local function init_board()
    local board = {}
    for y = 1, board_height do
        board[y] = {}
        for x = 1, board_width do
            board[y][x] = 0 -- 0 for empty, piece ID for placed blocks
        end
    end
    return board
end

local function can_place_piece(board, piece, px, py)
    for y = 1, #piece do
        for x = 1, #piece[1] do
            if piece[y][x] == 1 then
                local board_x = px + x - 1
                local board_y = py + y - 1
                if board_x < 1 or board_x > board_width or board_y > board_height or
                   (board_y >= 1 and board[board_y][board_x] ~= 0) then
                    return false
                end
            end
        end
    end
    return true
end

local function place_piece(board, piece, piece_id, px, py)
    for y = 1, #piece do
        for x = 1, #piece[1] do
            if piece[y][x] == 1 then
                local board_x = px + x - 1
                local board_y = py + y - 1
                if board_y >= 1 and board_y <= board_height then
                    board[board_y][board_x] = piece_id -- Store piece ID for color
                end
            end
        end
    end
end

local function clear_lines(board)
    local lines_cleared = 0
    local y = board_height
    while y >= 1 do
        local full = true
        for x = 1, board_width do
            if board[y][x] == 0 then
                full = false
                break
            end
        end
        if full then
            table.remove(board, y)
            table.insert(board, 1, {})
            for x = 1, board_width do
                board[1][x] = 0
            end
            lines_cleared = lines_cleared + 1
        else
            y = y - 1
        end
    end
    return lines_cleared
end

local function rotate_piece(piece, piece_id, px, py, board)
    local new_piece = {}
    for x = 1, #piece[1] do
        new_piece[x] = {}
        for y = 1, #piece do
            new_piece[x][#piece + 1 - y] = piece[y][x]
        end
    end
    if can_place_piece(board, new_piece, px, py) then
        return new_piece
    end
    return piece
end

function render(applet, board, piece, piece_id, px, py, score)
    local output = cursor_home
    output = output .. game_name .. " - Lines: " .. score .. "\r\n"
    output = output .. "+" .. string.rep("-", board_width * 2) .. "+\r\n"
    for y = 1, board_height do
        output = output .. "|"
        for x = 1, board_width do
            local char = "  "
            -- Current piece
            for py_idx = 1, #piece do
                for px_idx = 1, #piece[1] do
                    if piece[py_idx][px_idx] == 1 then
                        local board_x = px + px_idx - 1
                        local board_y = py + py_idx - 1
                        if board_x == x and board_y == y then
                            char = color_codes[piece_id] .. "[]" .. reset_color
                        end
                    end
                end
            end
            -- Placed blocks
            if board[y][x] ~= 0 then
                char = color_codes[board[y][x]] .. "[]" .. reset_color
            end
            output = output .. char
        end
        output = output .. "|\r\n"
    end
    output = output .. "+" .. string.rep("-", board_width * 2) .. "+\r\n"
    output = output .. "Use arrow keys to move, Up to rotate, q to quit"
    applet:send(output)
end

function handler(applet)
    local board = init_board()
    local piece_idx = math.random(#pieces)
    local current_piece = pieces[piece_idx].shape
    local piece_id = pieces[piece_idx].id
    local piece_x = math.floor(board_width / 2) - math.floor(#current_piece[1] / 2)
    local piece_y = 1
    local score = 0
    local game_over = false
    local delay = 500

    if not can_place_piece(board, current_piece, piece_x, piece_y) then
        game_over = true
    end

    applet:send(cursor_hide)
    applet:send(clear_screen)

    -- fall the piece by one line every delay
    local function fall_piece()
      while not game_over do
        piece_y = piece_y + 1
        if not can_place_piece(board, current_piece, piece_x, piece_y) then
            piece_y = piece_y - 1
            place_piece(board, current_piece, piece_id, piece_x, piece_y)
            score = score + clear_lines(board)
            piece_idx = math.random(#pieces)
            current_piece = pieces[piece_idx].shape
            piece_id = pieces[piece_idx].id
            piece_x = math.floor(board_width / 2) - math.floor(#current_piece[1] / 2)
            piece_y = 1
            if not can_place_piece(board, current_piece, piece_x, piece_y) then
                game_over = true
            end
        end
        core.msleep(delay)
      end
    end

    core.register_task(fall_piece)

    local function drop_piece()
        while can_place_piece(board, current_piece, piece_x, piece_y) do
            piece_y = piece_y + 1
        end
        piece_y = piece_y - 1
        place_piece(board, current_piece, piece_id, piece_x, piece_y)
        score = score + clear_lines(board)
        piece_idx = math.random(#pieces)
        current_piece = pieces[piece_idx].shape
        piece_id = pieces[piece_idx].id
        piece_x = math.floor(board_width / 2) - math.floor(#current_piece[1] / 2)
        piece_y = 1
        if not can_place_piece(board, current_piece, piece_x, piece_y) then
            game_over = true
        end
        render(applet, board, current_piece, piece_id, piece_x, piece_y, score)
    end

    while not game_over do
        render(applet, board, current_piece, piece_id, piece_x, piece_y, score)

        -- update the delay based on the score: 500 for 0 lines to 100ms for 100 lines.
        if score >= 100 then
          delay = 100
        else
          delay = 500 - 4*score
        end

        local input = applet:receive(1, delay)
        if input then
            if input == "" or input == "q" then
                game_over = true
            elseif input == "\27" then
                local a = applet:receive(1, delay)
                if a == "[" then
                    local b = applet:receive(1, delay)
                    if b == "A" then -- Up arrow (rotate clockwise)
                        current_piece = rotate_piece(current_piece, piece_id, piece_x, piece_y, board)
                    elseif b == "B" then -- Down arrow (full drop)
                        drop_piece()
                    elseif b == "C" then -- Right arrow
                        piece_x = piece_x + 1
                        if not can_place_piece(board, current_piece, piece_x, piece_y) then
                            piece_x = piece_x - 1
                        end
                    elseif b == "D" then -- Left arrow
                        piece_x = piece_x - 1
                        if not can_place_piece(board, current_piece, piece_x, piece_y) then
                            piece_x = piece_x + 1
                        end
                    end
                end
            end
        end
    end

    applet:send(clear_screen .. cursor_home .. "Game Over! Lines: " .. score .. "\r\n" .. cursor_show)
end

-- works as a TCP applet
core.register_service("trisdemo", "tcp", handler)

-- may also work on the CLI but requires an unbuffered handler
core.register_cli({"trisdemo"}, "Play a simple falling pieces game", handler)