Sat Apr 20 00:01:25 PDT 2002
[netwalk.git] / netwalk.e
blob1389b1f8d9b492e7be39609f35584d8c5f1b51ca
1 --TODO: similar code: get_neighbour, is_surrounded. need TUPLEs?
2 class NETWALK
3 inherit DIR_CONSTANT; SDL_CONSTANT; COLOR_TABLE; CONFIG
4 creation make
5 feature
6 tile_server_top : INTEGER is 1
7 tile_server_bottom : INTEGER is 2
8 tile_normal : INTEGER is 3
9 tile_terminal : INTEGER is 4
11 board : ARRAY2[TILE]
13 default_width : INTEGER is 10
14 default_height : INTEGER is 9
15 min_width : INTEGER is 5
16 max_width : INTEGER is 15
17 min_height : INTEGER is 4
18 max_height : INTEGER is 13
20 width : INTEGER
21 height : INTEGER
22 servertopi : INTEGER is
24 Result := width // 2
25 end
26 servertopj : INTEGER is
28 Result := height // 2 + 1
29 end
31 font : TTF_FONT is
33 Result := config.bigfont
34 end
36 mainfont : TTF_FONT is
38 Result := config.mainfont
39 end
41 win_image : IMAGE is
42 once
43 !!Result.make
44 Result.render_string("Well Done", font, white)
45 end
47 allow_wrap : BOOLEAN
49 seed : INTEGER
50 move_count : INTEGER
52 new_game_button : BUTTON
53 options_button : BUTTON
54 quit_button : BUTTON
56 options_window : OPTIONS_WINDOW
58 widget_list : LINKED_LIST[WIDGET]
60 add_widget(w : WIDGET) is
62 widget_list.add_last(w)
63 end
65 make is
66 local
67 c : COMMAND
69 !!widget_list.make
70 seed := 1
71 !!rand.make
72 ext_init
73 connected_pipe_color := green
74 disconnected_pipe_color := darkred
75 connected_terminal_color := cyan
76 disconnected_terminal_color := darkpurple
77 width := default_width
78 height := default_height
79 !!move_image.make
80 move_image.render_string("moves: " + move_count.to_string, mainfont, white)
81 !!best_image.make
82 best_image.render_string("par: " + best.to_string, mainfont, white)
84 !!new_game_button.make("New Game")
85 new_game_button.put_geometry(550, 20, 80, 20)
86 !COMMAND_NEW_GAME!c.make(Current)
87 new_game_button.put_command(c, new_game_button.signal_activate)
88 add_widget(new_game_button)
90 !!options_button.make("Options")
91 options_button.put_geometry(550, 60, 80, 20)
92 !COMMAND_OPTIONS!c.make(Current)
93 options_button.put_command(c, options_button.signal_activate)
94 add_widget(options_button)
96 !!quit_button.make("Quit")
97 quit_button.put_geometry(550, 100, 80, 20)
98 !COMMAND_QUIT!c.make(Current)
99 quit_button.put_command(c, quit_button.signal_activate)
100 add_widget(quit_button)
102 make_options_window
103 new_game
104 main_loop
107 make_options_window is
108 local
109 c : COMMAND
111 !!options_window.make
112 !COMMAND_OPTIONS_OK!c.make(Current)
113 options_window.put_command(c, options_window.signal_activate)
114 !COMMAND_OPTIONS_CANCEL!c.make(Current)
115 options_window.put_command(c, options_window.signal_cancel)
118 options_cancel is
119 require
120 options_window_showing
122 options_window_showing := False
125 options_ok is
126 require
127 options_window_showing
128 local
129 s : STRING
130 i : INTEGER
132 options_window_showing := False
133 allow_wrap := options_window.wrap_cb.value
134 s := options_window.width_tb.string
135 if s.is_integer then
136 i := s.to_integer
137 if i < min_width then
138 width := min_width
139 elseif i > max_width then
140 width := max_width
141 else
142 width := i
146 --TODO: similar code
147 s := options_window.height_tb.string
148 if s.is_integer then
149 i := s.to_integer
150 if i < min_height then
151 height := min_height
152 elseif i > max_height then
153 height := max_height
154 else
155 height := i
158 new_game
161 new_game is
163 rand.with_seed(seed)
164 !!board.make(1, width, 1, height)
165 generate_board
166 preprocess_board
167 reset_move_count
168 scramble_board
169 update_best_image
170 reset_move_count
171 game_state := state_playing
174 options is
176 options_window_showing := True
177 options_window.wrap_cb.put_value(allow_wrap)
178 options_window.width_tb.put_string(width.to_string)
179 options_window.height_tb.put_string(height.to_string)
182 state_none : INTEGER is 0
183 state_playing : INTEGER is 1
184 state_victory : INTEGER is 2
185 state_quit : INTEGER is 3
186 game_state : INTEGER
187 options_window_showing : BOOLEAN
189 main_loop is
190 local
191 e : EVENT
193 from
194 until game_state = state_quit
195 loop
196 seed := seed + 1
197 blank_screen
198 widget_list.do_all(agent {WIDGET}.update)
199 draw_border
200 if game_state = state_playing then
201 draw_board
202 if check_connections then
203 game_state := state_victory
206 if game_state = state_victory then
207 draw_board
208 win_image.blit(50, 400)
211 best_image.blit(10, 440)
212 move_image.blit(10, 460)
214 if options_window_showing then
215 options_window.update
218 ext_update_screen
219 e := poll_event
220 if e /= Void then
221 if options_window_showing then
222 options_window.process_event(e)
223 else
224 handle_event(e)
230 quit is
232 game_state := state_quit
235 handle_event(e : EVENT) is
237 inspect e.type
238 when sdl_keydown then
239 inspect e.i1
240 when sdlk_f2 then
241 new_game
242 else
244 when sdl_mousebuttondown then
245 handle_mbdown(e)
246 when sdl_quit then
247 quit
248 else
252 handle_mbdown(e : EVENT) is
253 local
254 i, j : INTEGER
255 it : ITERATOR[WIDGET]
257 it := widget_list.get_new_iterator
258 from it.start
259 until it.is_off
260 loop
261 if it.item.contains(e.x, e.y) then
262 it.item.process_event(e)
264 it.next
266 if game_state = state_playing then
267 i := e.x // cellwidth
268 j := e.y // cellheight
269 if on_board(i, j) then
270 if e.i1 = 1 then
271 rotateccw(i, j)
272 else
273 rotatecw(i, j)
279 move_image : IMAGE
280 best_image : IMAGE
282 reset_move_count is
284 move_count := 0
285 move_image.free
286 move_image.render_string("moves: " + move_count.to_string, mainfont, white)
289 tally_move is
291 move_count := move_count + 1
292 move_image.free
293 move_image.render_string("moves: " + move_count.to_string, mainfont, white)
296 update_best_image is
298 best_image.free
299 best_image.render_string("par: " + best.to_string, mainfont, white)
302 rotatecw(i, j : INTEGER) is
303 local
304 t : TILE
305 dir : INTEGER
306 bak : BOOLEAN
308 t := board.item(i, j)
309 if t /= Void then
310 if t = server_top or else t = server_bottom then
311 bak := server_top.neighbour.item(1)
312 server_top.neighbour.put(server_bottom.neighbour.item(4), 1)
313 server_bottom.neighbour.put(server_bottom.neighbour.item(3), 4)
314 server_bottom.neighbour.put(server_bottom.neighbour.item(2), 3)
315 server_bottom.neighbour.put(bak, 2)
316 else
317 bak := t.neighbour.item(4)
318 from dir := 4
319 until dir = 1
320 loop
321 t.neighbour.put(t.neighbour.item(dir - 1), dir)
322 dir := dir - 1
324 t.neighbour.put(bak, dir)
326 tally_move
330 rotateccw(i, j : INTEGER) is
331 local
332 t : TILE
333 dir : INTEGER
334 bak : BOOLEAN
336 t := board.item(i, j)
337 if t /= Void then
338 if t = server_top or else t = server_bottom then
339 bak := server_top.neighbour.item(1)
340 server_top.neighbour.put(server_bottom.neighbour.item(2), 1)
341 server_bottom.neighbour.put(server_bottom.neighbour.item(3), 2)
342 server_bottom.neighbour.put(server_bottom.neighbour.item(4), 3)
343 server_bottom.neighbour.put(bak, 4)
344 else
345 bak := t.neighbour.item(1)
346 from dir := 1
347 until dir = 4
348 loop
349 t.neighbour.put(t.neighbour.item(dir + 1), dir)
350 dir := dir + 1
352 t.neighbour.put(bak, dir)
354 tally_move
358 server_top, server_bottom : TILE
360 preprocess_board is
361 local
362 i, j : INTEGER
363 t : TILE
365 from i := 1
366 until i > width
367 loop
368 from j := 1
369 until j > height
370 loop
371 t := board.item(i, j)
372 if t /= Void then
373 t.count_neighbours
375 j := j + 1
377 i := i + 1
381 draw_board is
382 local
383 i, j : INTEGER
385 from i := 1
386 until i > width
387 loop
388 from j := 1
389 until j > height
390 loop
391 draw_tile(board.item(i, j))
392 j := j + 1
394 i := i + 1
398 draw_border is
399 local
400 i : INTEGER
402 from i := 1
403 until i > width + 1
404 loop
405 fill_rect(i * cellwidth, cellheight, 1, height * cellheight, blue)
406 i := i + 1
409 from i := 1
410 until i > height + 1
411 loop
412 fill_rect(cellwidth, i * cellheight, width * cellwidth, 1, blue)
413 i := i + 1
417 cellwidth : INTEGER is 32
418 cellheight : INTEGER is 32
419 uppipex : INTEGER is 13
420 uppipew : INTEGER is 6
421 uppipeh : INTEGER is 19
422 leftpipey : INTEGER is 13
423 leftpipew : INTEGER is 19
424 leftpipeh : INTEGER is 6
426 check_connections : BOOLEAN is
427 local
428 check_list : LINKED_LIST[TILE]
429 i, j : INTEGER
430 dir : INTEGER
431 t, t2 : TILE
433 from i := 1
434 until i > width
435 loop
436 from j := 1
437 until j > height
438 loop
439 t := board.item(i, j)
440 if t /= Void then
441 t.disconnect
443 j := j + 1
445 i := i + 1
448 !!check_list.make
449 check_list.add_last(server_top)
450 check_list.add_last(server_bottom)
451 server_top.connect
452 server_bottom.connect
454 from
455 until check_list.is_empty
456 loop
457 t := check_list.first
458 check_list.remove_first
459 from dir := 1
460 until dir > 4
461 loop
462 if t.neighbour.item(dir) then
463 t2 := get_neighbour(t, dir)
464 if t2 /= Void and then
465 t2.neighbour.item(dir_opposite(dir)) and then
466 not t2.is_connected then
467 t2.connect
468 check_list.add_last(t2)
471 dir := dir + 1
475 --victory check
476 Result := True
477 from i := 1
478 until i > width or else not Result
479 loop
480 from j := 1
481 until j > height or else not Result
482 loop
483 t := board.item(i, j)
484 if t /= Void and then not t.is_connected then
485 Result := False
487 j := j + 1
489 i := i + 1
493 maybe_wrapx(i : INTEGER) : INTEGER is
495 Result := i
496 if allow_wrap then
497 if i < 1 then
498 Result := i + width
499 elseif i > width then
500 Result := i - width
505 maybe_wrapy(i : INTEGER) : INTEGER is
507 Result := i
508 if allow_wrap then
509 if i < 1 then
510 Result := i + height
511 elseif i > height then
512 Result := i - height
517 get_neighbour(t : TILE; dir : INTEGER) : TILE is
518 local
519 x, y : INTEGER
520 x1, y1 : INTEGER
522 if dir = dir_up then
523 y1 := -1
524 elseif dir = dir_down then
525 y1 := 1
528 if dir = dir_left then
529 x1 := -1
530 elseif dir = dir_right then
531 x1 := 1
533 x := maybe_wrapx(t.x + x1)
534 y := maybe_wrapy(t.y + y1)
535 if on_board(x, y) then
536 Result := board.item(x, y)
540 on_board(x, y : INTEGER) : BOOLEAN is
542 Result := x >= 1 and then x <= width
543 and then y >= 1 and then y <= height
546 connected_pipe_color : COLOR
547 disconnected_pipe_color : COLOR
548 connected_terminal_color : COLOR
549 disconnected_terminal_color : COLOR
551 draw_tile(tile : TILE) is
552 local
553 x : INTEGER
554 y : INTEGER
555 c, c2 : COLOR
557 if tile /= Void then
558 x := tile.x * cellwidth
559 y := tile.y * cellheight
560 if tile.is_connected then
561 c := connected_pipe_color
562 c2 := connected_terminal_color
563 else
564 c := disconnected_pipe_color
565 c2 := disconnected_terminal_color
568 if tile.neighbour.item(dir_up) then
569 fill_rect(x + uppipex, y, uppipew, uppipeh, c)
571 if tile.neighbour.item(dir_left) then
572 fill_rect(x, y + leftpipey, leftpipew, leftpipeh, c)
574 if tile.neighbour.item(dir_right) then
575 fill_rect(x + uppipex, y + leftpipey, leftpipew, leftpipeh, c)
577 if tile.neighbour.item(dir_down) then
578 fill_rect(x + uppipex, y + leftpipey, uppipew, uppipeh, c)
581 inspect tile.type
582 when tile_server_top then
583 fill_rect(x + 7, y + 7, cellwidth - 12, cellheight - 6, blue)
584 when tile_server_bottom then
585 fill_rect(x + 7, y, cellwidth - 12, cellheight - 6, blue)
586 when tile_terminal then
587 fill_rect(x + 8, y + 8, 16, 16, c2)
588 else
594 ext_fill_rect(x, y, w, h : INTEGER; c : INTEGER) is
595 external "C"
598 wrap_flag : BOOLEAN
600 open_list : LINKED_LIST[TILE]
602 rand : STD_RAND
604 generate_board is
605 local
606 i : INTEGER
607 t : TILE
609 --place server
610 !!server_bottom.make_server_bottom
611 place_tile(server_bottom, servertopi, servertopj + 1)
612 !!server_top.make_server_top
613 place_tile(server_top, servertopi, servertopj)
615 --generate maze
616 !!open_list.make
617 open_list.add_last(server_top)
618 open_list.add_last(server_bottom)
620 from
621 until open_list.is_empty
622 loop
623 rand.next
624 i := rand.last_integer(open_list.count)
625 t := open_list @ i
626 rand.next
627 i := rand.last_integer(4)
628 try_extend(t, i)
632 best : INTEGER
634 scramble_board is
635 local
636 i, j : INTEGER
638 best := 0
639 from i := 1
640 until i > width
641 loop
642 from j := 1
643 until j > height
644 loop
645 if board.item(i, j) /= server_top then
646 rand.next
647 inspect rand.last_integer(4)
648 when 1 then
649 rotateccw(i, j)
650 best := best + 1
651 when 2 then
652 rotatecw(i, j)
653 best := best + 1
654 when 3 then
655 rotatecw(i, j)
656 rotatecw(i, j)
657 best := best + 2
658 --don't count reflections
659 if board.item(i, j) = server_bottom and then
660 server_top.neighbour.item(dir_up) = server_bottom.neighbour.item(dir_down) and then
661 server_bottom.neighbour.item(dir_left) = server_bottom.neighbour.item(dir_right) then
663 best := best - 2
664 elseif board.item(i, j).is_symmetric then
665 best := best - 2
667 else
670 j := j + 1
672 i := i + 1
676 try_extend(t : TILE; dir : INTEGER) is
677 local
678 x, y : INTEGER
679 x1, y1 : INTEGER
680 t2 : TILE
681 i : INTEGER
683 if dir = dir_up then
684 y1 := -1
685 elseif dir = dir_down then
686 y1 := 1
689 if dir = dir_left then
690 x1 := -1
691 elseif dir = dir_right then
692 x1 := 1
695 x := maybe_wrapx(t.x + x1)
696 y := maybe_wrapy(t.y + y1)
698 if on_board(x, y) then
699 t2 := board.item(x, y)
700 if t = server_top and then (dir = dir_left or else dir = dir_right) then
701 elseif t2 /= Void then
702 else
703 !!t2.make
704 place_tile(t2, x, y)
705 t.neighbour.put(True, dir)
706 t2.neighbour.put(True, dir_opposite(dir))
707 open_list.add_last(t2)
709 from i := open_list.lower
710 until i > open_list.upper
711 loop
712 if is_surrounded(open_list @ i) then
713 open_list.remove(i)
714 else
715 i := i + 1
722 is_surrounded(t : TILE) : BOOLEAN is
723 local
724 dir : INTEGER
725 x, y : INTEGER
726 x1, y1 : INTEGER
727 count : INTEGER
729 Result := True
730 from dir := 1
731 until dir > 4
732 loop
733 if t.neighbour.item(dir) then
734 count := count + 1
736 y1 := 0
737 if dir = dir_up then
738 y1 := -1
739 elseif dir = dir_down then
740 y1 := 1
743 x1 := 0
744 if dir = dir_left then
745 x1 := -1
746 elseif dir = dir_right then
747 x1 := 1
750 x := maybe_wrapx(t.x + x1)
751 y := maybe_wrapy(t.y + y1)
753 if on_board(x, y) then
754 if board.item(x, y) = Void then
755 if t = server_top then
756 if dir /= dir_left and then dir /= dir_right then
757 Result := False
759 else
760 Result := False
764 dir := dir + 1
766 if count >= 3 then
767 Result := True
771 place_tile(tile : TILE; x, y : INTEGER) is
773 tile.put_xy(x, y)
774 board.put(tile, x, y)
777 poll_event : EVENT is
778 local
779 em : EVENTMAKER
781 Result := ext_poll_event(em)
784 ext_init is
785 external "C"
788 ext_poll_event(em : EVENTMAKER) : EVENT is
789 external "C" alias "ext_poll_event"
792 ext_update_screen is
793 external "C"
796 fill_rect(x, y, w, h : INTEGER; c : COLOR) is
798 --ext_fill_rect(x + offsetx, y + offsety, w, h, c.to_integer)
799 ext_fill_rect(x, y, w, h, c.to_integer)
802 blank_screen is
804 fill_rect(0, 0, 640, 480, black)