1 --TODO: similar code: get_neighbour, is_surrounded. need TUPLEs?
3 inherit DIR_CONSTANT
; SDL_CONSTANT
; COLOR_TABLE
; CONFIG
8 tile_server_top
: INTEGER is 1
9 tile_server_bottom
: INTEGER is 2
10 tile_normal
: INTEGER is 3
11 tile_terminal
: INTEGER is 4
17 Result := current_setting
.width
22 Result := current_setting
.height
25 allow_wrap
: BOOLEAN is
27 Result := current_setting
.wrap
30 current_preset
: PRESET
31 current_setting
: SETTING
33 use_setting(s
: SETTING
) is
35 !!current_setting
.make
36 current_setting
.copy(s
)
39 servertopi
: INTEGER is
43 servertopj
: INTEGER is
45 Result := height
// 2 + 1
50 Result := config
.bigfont
53 mainfont
: TTF_FONT
is
55 Result := config
.mainfont
61 Result.render_string("Well Done", font
, white
)
67 new_game_button
: BUTTON
68 options_button
: BUTTON
72 options_window
: OPTIONS_WINDOW
73 newhs_window
: NEWHS_WINDOW
76 widget_list
: LINKED_LIST
[WIDGET
]
78 add_widget(w
: WIDGET
; x
, y
: INTEGER) is
80 widget_list
.add_last(w
)
84 elapsed_seconds
: INTEGER
85 elapsed_ticks
: INTEGER
95 seed
:= time
.hour
* 3600 + time
.minute
* 60 + time
.second
96 !!rand
.with_seed(seed
)
98 connected_pipe_color
:= green
99 disconnected_pipe_color
:= darkred
100 connected_terminal_color
:= cyan
101 disconnected_terminal_color
:= darkpurple
102 current_preset
:= config
.get_preset(config
.default_preset
)
103 use_setting(current_preset
.setting
)
113 !!new_game_button
.make("New Game")
114 new_game_button
.put_size(80, 20)
115 !COMMAND_NEW_GAME
!c
.make(Current)
116 new_game_button
.put_command(c
, new_game_button
.signal_activate
)
117 add_widget(new_game_button
, 550, 20)
119 !!options_button
.make("Options")
120 options_button
.put_size(80, 20)
121 !COMMAND_OPTIONS
!c
.make(Current)
122 options_button
.put_command(c
, options_button
.signal_activate
)
123 add_widget(options_button
, 550, 60)
125 !!hs_button
.make("High Scores")
126 hs_button
.put_size(80, 20)
127 !COMMAND_SHOW_HS
!c
.make(Current)
128 hs_button
.put_command(c
, hs_button
.signal_activate
)
129 add_widget(hs_button
, 550, 100)
131 !!quit_button
.make("Quit")
132 quit_button
.put_size(80, 20)
133 !COMMAND_QUIT
!c
.make(Current)
134 quit_button
.put_command(c
, quit_button
.signal_activate
)
135 add_widget(quit_button
, 550, 140)
138 io_score
.load(config
.score_file
)
139 io_score
.into_preset_list(config
.preset_list
)
149 make_options_window
is
153 !!options_window
.make
154 !COMMAND_OPTIONS_OK
!c
.make(Current)
155 options_window
.put_command(c
, options_window
.signal_activate
)
156 !COMMAND_CLOSE_CURRENT
!c
.make(Current)
157 options_window
.put_command(c
, options_window
.signal_cancel
)
158 options_window
.put_xy(200, 100)
159 options_window
.put_preset_list(config
.preset_list
)
167 !COMMAND_NEWHS_OK
!c
.make(Current)
168 newhs_window
.put_command(c
, newhs_window
.signal_activate
)
169 newhs_window
.put_xy(200, 100)
177 !COMMAND_CLOSE_CURRENT
!c
.make(Current)
178 hs_window
.put_command(c
, hs_window
.signal_activate
)
179 hs_window
.put_xy(200, 100)
180 hs_window
.init_scores(config
.preset_list
)
183 close_current_window
is
185 current_window
/= Void
187 current_window
:= Void
192 current_window
= Void
194 current_window
:= hs_window
199 current_window
= newhs_window
203 current_window
:= Void
204 s
:= newhs_window
.name_tb
.string
205 current_preset
.hiscore
.put_name(s
)
206 hs_window
.update_score(current_preset
)
207 io_score
.from_preset_list(config
.preset_list
)
208 io_score
.save(config
.score_file
)
213 current_window
= options_window
215 current_window
:= Void
216 options_window
.update_setting
217 if current_preset
/= options_window
.preset
then
218 current_preset
:= options_window
.preset
219 use_setting(options_window
.setting
)
221 elseif not current_setting
.is_equal(options_window
.setting
) then
222 use_setting(options_window
.setting
)
230 !!board
.make(1, width
, 1, height
)
237 game_state
:= state_playing
238 last_ticks
:= get_ticks
246 current_window
:= options_window
247 options_window
.put_info(current_preset
, current_setting
)
250 state_none
: INTEGER is 0
251 state_playing
: INTEGER is 1
252 state_victory
: INTEGER is 2
253 state_quit
: INTEGER is 3
256 current_window
: WINDOW
264 until game_state
= state_quit
269 widget_list
.do_all(agent {WIDGET
}.update
)
271 if game_state
= state_playing
then
273 elapsed_ticks
:= elapsed_ticks
+ i
- last_ticks
275 i
:= elapsed_ticks
// 1000
276 if i
> elapsed_seconds
then
283 if is_victorious
then
284 game_state
:= state_victory
288 if game_state
= state_victory
then
290 win_image
.blit(10, 0)
293 best_image
.blit(10, 420)
294 move_image
.blit(10, 440)
295 time_image
.blit(10, 460)
297 if current_window
/= Void
then
298 current_window
.update
304 if current_window
/= Void
then
305 current_window
.process_event(e
)
319 score
:= move_count
- best
320 if current_preset
/= Void
then
321 hiscore
:= current_preset
.hiscore
322 if hiscore
= Void
then
324 elseif hiscore
.time
> elapsed_seconds
then
326 elseif hiscore
.time
= elapsed_seconds
and then hiscore
.score
> score
then
330 !!hiscore
.make("Anonymous", score
, elapsed_seconds
)
331 current_preset
.put_hiscore(hiscore
)
332 current_window
:= newhs_window
333 newhs_window
.name_tb
.put_string(hiscore
.name
)
340 game_state
:= state_quit
343 handle_event(e
: EVENT
) is
346 when sdl_keydown
then
352 when sdl_mousebuttondown
then
360 handle_mbdown(e
: EVENT
) is
363 it
: ITERATOR
[WIDGET
]
365 it
:= widget_list
.get_new_iterator
369 if it
.item
.contains(e
.x
, e
.y
) then
370 it
.item
.process_event(e
)
374 if game_state
= state_playing
then
375 i
:= e
.x
// cellwidth
376 j
:= e
.y
// cellheight
377 if on_board(i
, j
) then
395 move_image
.render_string("moves: " + move_count
.to_string
, mainfont
, white
)
400 move_count
:= move_count
+ 1
402 move_image
.render_string("moves: " + move_count
.to_string
, mainfont
, white
)
408 best_image
.render_string("par: " + best
.to_string
, mainfont
, white
)
414 time_image
.render_string("time: " + elapsed_seconds
.to_string
, mainfont
, white
)
417 rotatecw(i
, j
: INTEGER) is
423 t
:= board
.item(i
, j
)
425 if t
= server_top
or else t
= server_bottom
then
426 bak
:= server_top
.neighbour
.item(1)
427 server_top
.neighbour
.put(server_bottom
.neighbour
.item(4), 1)
428 server_bottom
.neighbour
.put(server_bottom
.neighbour
.item(3), 4)
429 server_bottom
.neighbour
.put(server_bottom
.neighbour
.item(2), 3)
430 server_bottom
.neighbour
.put(bak
, 2)
432 bak
:= t
.neighbour
.item(4)
436 t
.neighbour
.put(t
.neighbour
.item(dir
- 1), dir
)
439 t
.neighbour
.put(bak
, dir
)
445 rotateccw(i
, j
: INTEGER) is
451 t
:= board
.item(i
, j
)
453 if t
= server_top
or else t
= server_bottom
then
454 bak
:= server_top
.neighbour
.item(1)
455 server_top
.neighbour
.put(server_bottom
.neighbour
.item(2), 1)
456 server_bottom
.neighbour
.put(server_bottom
.neighbour
.item(3), 2)
457 server_bottom
.neighbour
.put(server_bottom
.neighbour
.item(4), 3)
458 server_bottom
.neighbour
.put(bak
, 4)
460 bak
:= t
.neighbour
.item(1)
464 t
.neighbour
.put(t
.neighbour
.item(dir
+ 1), dir
)
467 t
.neighbour
.put(bak
, dir
)
473 server_top
, server_bottom
: TILE
486 t
:= board
.item(i
, j
)
506 draw_tile(board
.item(i
, j
))
520 fill_rect(i
* cellwidth
, cellheight
, 1, height
* cellheight
, blue
)
527 fill_rect(cellwidth
, i
* cellheight
, width
* cellwidth
, 1, blue
)
532 cellwidth
: INTEGER is 32
533 cellheight
: INTEGER is 32
534 uppipex
: INTEGER is 13
535 uppipew
: INTEGER is 6
536 uppipeh
: INTEGER is 19
537 leftpipey
: INTEGER is 13
538 leftpipew
: INTEGER is 19
539 leftpipeh
: INTEGER is 6
543 check_list
: LINKED_LIST
[TILE
]
554 t
:= board
.item(i
, j
)
564 check_list
.add_last(server_top
)
565 check_list
.add_last(server_bottom
)
567 server_bottom
.connect
570 until check_list
.is_empty
572 t
:= check_list
.first
573 check_list
.remove_first
577 if t
.neighbour
.item(dir
) then
578 t2
:= get_neighbour(t
, dir
)
579 if t2
/= Void
and then
580 t2
.neighbour
.item(dir_opposite(dir
)) and then
581 not t2
.is_connected
then
583 check_list
.add_last(t2
)
591 is_victorious
: BOOLEAN is
598 until i
> width
or else not Result
601 until j
> height
or else not Result
603 t
:= board
.item(i
, j
)
604 if t
/= Void
and then not t
.is_connected
then
613 maybe_wrapx(i
: INTEGER) : INTEGER is
619 elseif i
> width
then
625 maybe_wrapy(i
: INTEGER) : INTEGER is
631 elseif i
> height
then
637 get_neighbour(t
: TILE
; dir
: INTEGER) : TILE
is
644 elseif dir
= dir_down
then
648 if dir
= dir_left
then
650 elseif dir
= dir_right
then
653 x
:= maybe_wrapx(t
.x
+ x1
)
654 y
:= maybe_wrapy(t
.y
+ y1
)
655 if on_board(x
, y
) then
656 Result := board
.item(x
, y
)
660 on_board(x
, y
: INTEGER) : BOOLEAN is
662 Result := x
>= 1 and then x
<= width
663 and then y
>= 1 and then y
<= height
666 connected_pipe_color
: COLOR
667 disconnected_pipe_color
: COLOR
668 connected_terminal_color
: COLOR
669 disconnected_terminal_color
: COLOR
671 draw_tile(tile
: TILE
) is
678 x
:= tile
.x
* cellwidth
679 y
:= tile
.y
* cellheight
680 if tile
.is_connected
then
681 c
:= connected_pipe_color
682 c2
:= connected_terminal_color
684 c
:= disconnected_pipe_color
685 c2
:= disconnected_terminal_color
688 if tile
.neighbour
.item(dir_up
) then
689 fill_rect(x
+ uppipex
, y
, uppipew
, uppipeh
, c
)
691 if tile
.neighbour
.item(dir_left
) then
692 fill_rect(x
, y
+ leftpipey
, leftpipew
, leftpipeh
, c
)
694 if tile
.neighbour
.item(dir_right
) then
695 fill_rect(x
+ uppipex
, y
+ leftpipey
, leftpipew
, leftpipeh
, c
)
697 if tile
.neighbour
.item(dir_down
) then
698 fill_rect(x
+ uppipex
, y
+ leftpipey
, uppipew
, uppipeh
, c
)
702 when tile_server_top
then
703 fill_rect(x
+ 7, y
+ 7, cellwidth
- 12, cellheight
- 6, blue
)
704 when tile_server_bottom
then
705 fill_rect(x
+ 7, y
, cellwidth
- 12, cellheight
- 6, blue
)
706 when tile_terminal
then
707 fill_rect(x
+ 8, y
+ 8, 16, 16, c2
)
714 ext_fill_rect(x
, y
, w
, h
: INTEGER; c
: INTEGER) is
720 open_list
: LINKED_LIST
[TILE
]
730 !!server_bottom
.make_server_bottom
731 place_tile(server_bottom
, servertopi
, servertopj
+ 1)
732 !!server_top
.make_server_top
733 place_tile(server_top
, servertopi
, servertopj
)
737 open_list
.add_last(server_top
)
738 open_list
.add_last(server_bottom
)
741 until open_list
.is_empty
744 i
:= rand
.last_integer(open_list
.count
)
747 i
:= rand
.last_integer(4)
766 if board
.item(i
, j
) /= server_top
then
769 if board
.item(i
, j
) = server_bottom
and then
770 server_top
.neighbour
.item(dir_up
) = server_bottom
.neighbour
.item(dir_down
) and then
771 server_bottom
.neighbour
.item(dir_left
) = server_bottom
.neighbour
.item(dir_right
) then
773 if server_top
.neighbour
.item(dir_up
) = server_bottom
.neighbour
.item(dir_right
) then
776 elseif board
.item(i
, j
).is_symmetric
then
781 inspect rand
.last_integer(4)
807 try_extend(t
: TILE
; dir
: INTEGER) is
816 elseif dir
= dir_down
then
820 if dir
= dir_left
then
822 elseif dir
= dir_right
then
826 x
:= maybe_wrapx(t
.x
+ x1
)
827 y
:= maybe_wrapy(t
.y
+ y1
)
829 if on_board(x
, y
) then
830 t2
:= board
.item(x
, y
)
831 if t
= server_top
and then (dir
= dir_left
or else dir
= dir_right
) then
832 elseif t2
/= Void
then
836 t
.neighbour
.put(True, dir
)
837 t2
.neighbour
.put(True, dir_opposite(dir
))
838 open_list
.add_last(t2
)
840 from i
:= open_list
.lower
841 until i
> open_list
.upper
843 if is_surrounded(open_list @ i
) then
853 is_surrounded(t
: TILE
) : BOOLEAN is
864 if t
.neighbour
.item(dir
) then
870 elseif dir
= dir_down
then
875 if dir
= dir_left
then
877 elseif dir
= dir_right
then
881 x
:= maybe_wrapx(t
.x
+ x1
)
882 y
:= maybe_wrapy(t
.y
+ y1
)
884 if on_board(x
, y
) then
885 if board
.item(x
, y
) = Void
then
886 if t
= server_top
then
887 if dir
/= dir_left
and then dir
/= dir_right
then
902 place_tile(tile
: TILE
; x
, y
: INTEGER) is
905 board
.put(tile
, x
, y
)
908 poll_event
: EVENT
is
912 Result := ext_poll_event(em
)
919 ext_poll_event(em
: EVENTMAKER
) : EVENT
is
920 external "C" alias "ext_poll_event"
923 get_ticks
: INTEGER is
924 external "C" alias "ext_get_ticks"
931 fill_rect(x
, y
, w
, h
: INTEGER; c
: COLOR
) is
933 --ext_fill_rect(x + offsetx, y + offsety, w, h, c.to_integer)
934 ext_fill_rect(x
, y
, w
, h
, c
.to_integer
)
939 fill_rect(0, 0, 640, 480, black
)