63
63
cur_sample = 0 # Current selected sample.
64
64
frame_times = [time .perf_counter ()]
65
65
frame_length = [0.0 ]
66
+ START_TIME = time .perf_counter ()
67
+
68
+
69
+ def _get_elapsed_time () -> float :
70
+ """Return time passed since the start of the program."""
71
+ return time .perf_counter () - START_TIME
66
72
67
73
68
74
class Sample (tcod .event .EventDispatch [None ]):
@@ -207,13 +213,13 @@ def __init__(self) -> None:
207
213
)
208
214
209
215
def on_enter (self ) -> None :
210
- self .counter = time . perf_counter ()
216
+ self .counter = _get_elapsed_time ()
211
217
# get a "screenshot" of the current sample screen
212
218
sample_console .blit (dest = self .screenshot )
213
219
214
220
def on_draw (self ) -> None :
215
- if time . perf_counter () - self .counter >= 1 :
216
- self .counter = time . perf_counter ()
221
+ if _get_elapsed_time () - self .counter >= 1 :
222
+ self .counter = _get_elapsed_time ()
217
223
self .x += self .x_dir
218
224
self .y += self .y_dir
219
225
if self .x == sample_console .width / 2 + 5 :
@@ -396,8 +402,8 @@ def get_noise(self) -> tcod.noise.Noise:
396
402
)
397
403
398
404
def on_draw (self ) -> None :
399
- self .dx = time . perf_counter () * 0.25
400
- self .dy = time . perf_counter () * 0.25
405
+ self .dx = _get_elapsed_time () * 0.25
406
+ self .dy = _get_elapsed_time () * 0.25
401
407
for y in range (2 * sample_console .height ):
402
408
for x in range (2 * sample_console .width ):
403
409
f = [
@@ -518,7 +524,7 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None:
518
524
"##############################################" ,
519
525
)
520
526
521
- SAMPLE_MAP : NDArray [Any ] = np .array ([list ( line ) for line in SAMPLE_MAP_ ]).transpose ()
527
+ SAMPLE_MAP : NDArray [Any ] = np .array ([[ ord ( c ) for c in line ] for line in SAMPLE_MAP_ ]).transpose ()
522
528
523
529
FOV_ALGO_NAMES = (
524
530
"BASIC " ,
@@ -555,17 +561,17 @@ def __init__(self) -> None:
555
561
map_shape = (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
556
562
557
563
self .walkable : NDArray [np .bool_ ] = np .zeros (map_shape , dtype = bool , order = "F" )
558
- self .walkable [:] = SAMPLE_MAP [:] == " "
564
+ self .walkable [:] = SAMPLE_MAP [:] == ord ( " " )
559
565
560
566
self .transparent : NDArray [np .bool_ ] = np .zeros (map_shape , dtype = bool , order = "F" )
561
- self .transparent [:] = self .walkable [:] | (SAMPLE_MAP == "=" )
567
+ self .transparent [:] = self .walkable [:] | (SAMPLE_MAP [:] == ord ( "=" ) )
562
568
563
569
# Lit background colors for the map.
564
570
self .light_map_bg : NDArray [np .uint8 ] = np .full (SAMPLE_MAP .shape , LIGHT_GROUND , dtype = "3B" )
565
- self .light_map_bg [SAMPLE_MAP [:] == "#" ] = LIGHT_WALL
571
+ self .light_map_bg [SAMPLE_MAP [:] == ord ( "#" ) ] = LIGHT_WALL
566
572
# Dark background colors for the map.
567
573
self .dark_map_bg : NDArray [np .uint8 ] = np .full (SAMPLE_MAP .shape , DARK_GROUND , dtype = "3B" )
568
- self .dark_map_bg [SAMPLE_MAP [:] == "#" ] = DARK_WALL
574
+ self .dark_map_bg [SAMPLE_MAP [:] == ord ( "#" ) ] = DARK_WALL
569
575
570
576
def draw_ui (self ) -> None :
571
577
sample_console .print (
@@ -586,8 +592,8 @@ def on_draw(self) -> None:
586
592
self .draw_ui ()
587
593
sample_console .print (self .player_x , self .player_y , "@" )
588
594
# Draw windows.
589
- sample_console .rgb ["ch" ][SAMPLE_MAP == "=" ] = 0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL
590
- sample_console .rgb ["fg" ][SAMPLE_MAP == "=" ] = BLACK
595
+ sample_console .rgb ["ch" ][SAMPLE_MAP [:] == ord ( "=" ) ] = 0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL
596
+ sample_console .rgb ["fg" ][SAMPLE_MAP [:] == ord ( "=" ) ] = BLACK
591
597
592
598
# Get a 2D boolean array of visible cells.
593
599
fov = tcod .map .compute_fov (
@@ -600,7 +606,7 @@ def on_draw(self) -> None:
600
606
601
607
if self .torch :
602
608
# Derive the touch from noise based on the current time.
603
- torch_t = time . perf_counter () * 5
609
+ torch_t = _get_elapsed_time () * 5
604
610
# Randomize the light position between -1.5 and 1.5
605
611
torch_x = self .player_x + self .noise .get_point (torch_t ) * 1.5
606
612
torch_y = self .player_y + self .noise .get_point (torch_t + 11 ) * 1.5
@@ -632,7 +638,11 @@ def on_draw(self) -> None:
632
638
# Linear interpolation between colors.
633
639
sample_console .rgb ["bg" ] = dark_bg + (light_bg - dark_bg ) * light [..., np .newaxis ]
634
640
else :
635
- sample_console .bg [...] = np .where (fov [:, :, np .newaxis ], self .light_map_bg , self .dark_map_bg )
641
+ sample_console .bg [...] = np .select (
642
+ condlist = [fov [:, :, np .newaxis ]],
643
+ choicelist = [self .light_map_bg ],
644
+ default = self .dark_map_bg ,
645
+ )
636
646
637
647
def ev_keydown (self , event : tcod .event .KeyDown ) -> None :
638
648
MOVE_KEYS = { # noqa: N806
@@ -665,174 +675,89 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None:
665
675
666
676
class PathfindingSample (Sample ):
667
677
def __init__ (self ) -> None :
678
+ """Initialize this sample."""
668
679
self .name = "Path finding"
669
680
670
- self .px = 20
671
- self .py = 10
672
- self .dx = 24
673
- self .dy = 1
674
- self .dijkstra_dist = 0.0
681
+ self .player_x = 20
682
+ self .player_y = 10
683
+ self .dest_x = 24
684
+ self .dest_y = 1
675
685
self .using_astar = True
676
- self .recalculate = False
677
686
self .busy = 0.0
678
- self .old_char = " "
687
+ self .cost = SAMPLE_MAP .T [:] == ord (" " )
688
+ self .graph = tcod .path .SimpleGraph (cost = self .cost , cardinal = 70 , diagonal = 99 )
689
+ self .pathfinder = tcod .path .Pathfinder (graph = self .graph )
679
690
680
- self .map = tcod .map .Map (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
681
- for y in range (SAMPLE_SCREEN_HEIGHT ):
682
- for x in range (SAMPLE_SCREEN_WIDTH ):
683
- if SAMPLE_MAP [x , y ] == " " :
684
- # ground
685
- self .map .walkable [y , x ] = True
686
- self .map .transparent [y , x ] = True
687
- elif SAMPLE_MAP [x , y ] == "=" :
688
- # window
689
- self .map .walkable [y , x ] = False
690
- self .map .transparent [y , x ] = True
691
- self .path = tcod .path .AStar (self .map )
692
- self .dijkstra = tcod .path .Dijkstra (self .map )
691
+ self .background_console = tcod .console .Console (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
692
+
693
+ # draw the dungeon
694
+ self .background_console .rgb ["fg" ] = BLACK
695
+ self .background_console .rgb ["bg" ] = DARK_GROUND
696
+ self .background_console .rgb ["bg" ][SAMPLE_MAP .T [:] == ord ("#" )] = DARK_WALL
697
+ self .background_console .rgb ["ch" ][SAMPLE_MAP .T [:] == ord ("=" )] = ord ("═" )
693
698
694
699
def on_enter (self ) -> None :
695
- # we draw the foreground only the first time.
696
- # during the player movement, only the @ is redrawn.
697
- # the rest impacts only the background color
698
- # draw the help text & player @
699
- sample_console .clear ()
700
- sample_console .ch [self .dx , self .dy ] = ord ("+" )
701
- sample_console .fg [self .dx , self .dy ] = WHITE
702
- sample_console .ch [self .px , self .py ] = ord ("@" )
703
- sample_console .fg [self .px , self .py ] = WHITE
704
- sample_console .print (
705
- 1 ,
706
- 1 ,
707
- "IJKL / mouse :\n move destination\n TAB : A*/dijkstra" ,
708
- fg = WHITE ,
709
- bg = None ,
710
- )
711
- sample_console .print (1 , 4 , "Using : A*" , fg = WHITE , bg = None )
712
- # draw windows
713
- for y in range (SAMPLE_SCREEN_HEIGHT ):
714
- for x in range (SAMPLE_SCREEN_WIDTH ):
715
- if SAMPLE_MAP [x , y ] == "=" :
716
- libtcodpy .console_put_char (sample_console , x , y , libtcodpy .CHAR_DHLINE , libtcodpy .BKGND_NONE )
717
- self .recalculate = True
700
+ """Do nothing."""
718
701
719
702
def on_draw (self ) -> None :
720
- if self .recalculate :
721
- if self .using_astar :
722
- libtcodpy .path_compute (self .path , self .px , self .py , self .dx , self .dy )
723
- else :
724
- self .dijkstra_dist = 0.0
725
- # compute dijkstra grid (distance from px,py)
726
- libtcodpy .dijkstra_compute (self .dijkstra , self .px , self .py )
727
- # get the maximum distance (needed for rendering)
728
- for y in range (SAMPLE_SCREEN_HEIGHT ):
729
- for x in range (SAMPLE_SCREEN_WIDTH ):
730
- d = libtcodpy .dijkstra_get_distance (self .dijkstra , x , y )
731
- self .dijkstra_dist = max (d , self .dijkstra_dist )
732
- # compute path from px,py to dx,dy
733
- libtcodpy .dijkstra_path_set (self .dijkstra , self .dx , self .dy )
734
- self .recalculate = False
735
- self .busy = 0.2
703
+ """Recompute and render pathfinding."""
704
+ self .pathfinder = tcod .path .Pathfinder (graph = self .graph )
705
+ # self.pathfinder.clear() # Known issues, needs fixing # noqa: ERA001
706
+ self .pathfinder .add_root ((self .player_y , self .player_x ))
707
+
736
708
# draw the dungeon
737
- for y in range (SAMPLE_SCREEN_HEIGHT ):
738
- for x in range (SAMPLE_SCREEN_WIDTH ):
739
- if SAMPLE_MAP [x , y ] == "#" :
740
- libtcodpy .console_set_char_background (sample_console , x , y , DARK_WALL , libtcodpy .BKGND_SET )
741
- else :
742
- libtcodpy .console_set_char_background (sample_console , x , y , DARK_GROUND , libtcodpy .BKGND_SET )
709
+ self .background_console .blit (dest = sample_console )
710
+
711
+ sample_console .print (self .dest_x , self .dest_y , "+" , fg = WHITE )
712
+ sample_console .print (self .player_x , self .player_y , "@" , fg = WHITE )
713
+ sample_console .print (1 , 1 , "IJKL / mouse :\n move destination\n TAB : A*/dijkstra" , fg = WHITE , bg = None )
714
+ sample_console .print (1 , 4 , "Using : A*" , fg = WHITE , bg = None )
715
+
716
+ if not self .using_astar :
717
+ self .pathfinder .resolve (goal = None )
718
+ reachable = self .pathfinder .distance != np .iinfo (self .pathfinder .distance .dtype ).max
719
+
720
+ # draw distance from player
721
+ dijkstra_max_dist = float (self .pathfinder .distance [reachable ].max ())
722
+ np .array (self .pathfinder .distance , copy = True , dtype = np .float32 )
723
+ interpolate = self .pathfinder .distance [reachable ] * 0.9 / dijkstra_max_dist
724
+ color_delta = (np .array (DARK_GROUND ) - np .array (LIGHT_GROUND )).astype (np .float32 )
725
+ sample_console .rgb .T ["bg" ][reachable ] = np .array (LIGHT_GROUND ) + interpolate [:, np .newaxis ] * color_delta
726
+
743
727
# draw the path
744
- if self .using_astar :
745
- for i in range (libtcodpy .path_size (self .path )):
746
- x , y = libtcodpy .path_get (self .path , i )
747
- libtcodpy .console_set_char_background (sample_console , x , y , LIGHT_GROUND , libtcodpy .BKGND_SET )
748
- else :
749
- for y in range (SAMPLE_SCREEN_HEIGHT ):
750
- for x in range (SAMPLE_SCREEN_WIDTH ):
751
- if SAMPLE_MAP [x , y ] != "#" :
752
- libtcodpy .console_set_char_background (
753
- sample_console ,
754
- x ,
755
- y ,
756
- libtcodpy .color_lerp ( # type: ignore[arg-type]
757
- LIGHT_GROUND ,
758
- DARK_GROUND ,
759
- 0.9 * libtcodpy .dijkstra_get_distance (self .dijkstra , x , y ) / self .dijkstra_dist ,
760
- ),
761
- libtcodpy .BKGND_SET ,
762
- )
763
- for i in range (libtcodpy .dijkstra_size (self .dijkstra )):
764
- x , y = libtcodpy .dijkstra_get (self .dijkstra , i )
765
- libtcodpy .console_set_char_background (sample_console , x , y , LIGHT_GROUND , libtcodpy .BKGND_SET )
728
+ path = self .pathfinder .path_to ((self .dest_y , self .dest_x ))[1 :, ::- 1 ]
729
+ sample_console .rgb ["bg" ][tuple (path .T )] = LIGHT_GROUND
766
730
767
731
# move the creature
768
732
self .busy -= frame_length [- 1 ]
769
733
if self .busy <= 0.0 :
770
734
self .busy = 0.2
771
- if self .using_astar :
772
- if not libtcodpy .path_is_empty (self .path ):
773
- libtcodpy .console_put_char (sample_console , self .px , self .py , " " , libtcodpy .BKGND_NONE )
774
- self .px , self .py = libtcodpy .path_walk (self .path , True ) # type: ignore[assignment]
775
- libtcodpy .console_put_char (sample_console , self .px , self .py , "@" , libtcodpy .BKGND_NONE )
776
- elif not libtcodpy .dijkstra_is_empty (self .dijkstra ):
777
- libtcodpy .console_put_char (sample_console , self .px , self .py , " " , libtcodpy .BKGND_NONE )
778
- self .px , self .py = libtcodpy .dijkstra_path_walk (self .dijkstra ) # type: ignore[assignment]
779
- libtcodpy .console_put_char (sample_console , self .px , self .py , "@" , libtcodpy .BKGND_NONE )
780
- self .recalculate = True
735
+ if len (path ):
736
+ self .player_x = int (path .item (0 , 0 ))
737
+ self .player_y = int (path .item (0 , 1 ))
781
738
782
739
def ev_keydown (self , event : tcod .event .KeyDown ) -> None :
783
- if event .sym == tcod .event .KeySym .i and self .dy > 0 :
784
- # destination move north
785
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
786
- self .dy -= 1
787
- self .old_char = sample_console .ch [self .dx , self .dy ]
788
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
789
- if SAMPLE_MAP [self .dx , self .dy ] == " " :
790
- self .recalculate = True
791
- elif event .sym == tcod .event .KeySym .k and self .dy < SAMPLE_SCREEN_HEIGHT - 1 :
792
- # destination move south
793
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
794
- self .dy += 1
795
- self .old_char = sample_console .ch [self .dx , self .dy ]
796
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
797
- if SAMPLE_MAP [self .dx , self .dy ] == " " :
798
- self .recalculate = True
799
- elif event .sym == tcod .event .KeySym .j and self .dx > 0 :
800
- # destination move west
801
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
802
- self .dx -= 1
803
- self .old_char = sample_console .ch [self .dx , self .dy ]
804
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
805
- if SAMPLE_MAP [self .dx , self .dy ] == " " :
806
- self .recalculate = True
807
- elif event .sym == tcod .event .KeySym .l and self .dx < SAMPLE_SCREEN_WIDTH - 1 :
808
- # destination move east
809
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
810
- self .dx += 1
811
- self .old_char = sample_console .ch [self .dx , self .dy ]
812
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
813
- if SAMPLE_MAP [self .dx , self .dy ] == " " :
814
- self .recalculate = True
740
+ """Handle movement and UI."""
741
+ if event .sym == tcod .event .KeySym .i and self .dest_y > 0 : # destination move north
742
+ self .dest_y -= 1
743
+ elif event .sym == tcod .event .KeySym .k and self .dest_y < SAMPLE_SCREEN_HEIGHT - 1 : # destination move south
744
+ self .dest_y += 1
745
+ elif event .sym == tcod .event .KeySym .j and self .dest_x > 0 : # destination move west
746
+ self .dest_x -= 1
747
+ elif event .sym == tcod .event .KeySym .l and self .dest_x < SAMPLE_SCREEN_WIDTH - 1 : # destination move east
748
+ self .dest_x += 1
815
749
elif event .sym == tcod .event .KeySym .TAB :
816
750
self .using_astar = not self .using_astar
817
- if self .using_astar :
818
- libtcodpy .console_print (sample_console , 1 , 4 , "Using : A* " )
819
- else :
820
- libtcodpy .console_print (sample_console , 1 , 4 , "Using : Dijkstra" )
821
- self .recalculate = True
822
751
else :
823
752
super ().ev_keydown (event )
824
753
825
754
def ev_mousemotion (self , event : tcod .event .MouseMotion ) -> None :
755
+ """Move destination via mouseover."""
826
756
mx = event .tile .x - SAMPLE_SCREEN_X
827
757
my = event .tile .y - SAMPLE_SCREEN_Y
828
- if 0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT and (self .dx != mx or self .dy != my ):
829
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
830
- self .dx = mx
831
- self .dy = my
832
- self .old_char = sample_console .ch [self .dx , self .dy ]
833
- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
834
- if SAMPLE_MAP [self .dx , self .dy ] == " " :
835
- self .recalculate = True
758
+ if 0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT :
759
+ self .dest_x = mx
760
+ self .dest_y = my
836
761
837
762
838
763
#############################################
@@ -1044,7 +969,7 @@ def on_draw(self) -> None:
1044
969
y = sample_console .height / 2
1045
970
scalex = 0.2 + 1.8 * (1.0 + math .cos (time .time () / 2 )) / 2.0
1046
971
scaley = scalex
1047
- angle = time . perf_counter ()
972
+ angle = _get_elapsed_time ()
1048
973
if int (time .time ()) % 2 :
1049
974
# split the color channels of circle.png
1050
975
# the red channel
@@ -1529,7 +1454,7 @@ def draw_stats() -> None:
1529
1454
root_console .print (
1530
1455
root_console .width ,
1531
1456
47 ,
1532
- f"elapsed : { int (time . perf_counter () * 1000 ):8d} ms { time . perf_counter ():5.2f} s" ,
1457
+ f"elapsed : { int (_get_elapsed_time () * 1000 ):8d} ms { _get_elapsed_time ():5.2f} s" ,
1533
1458
fg = GREY ,
1534
1459
alignment = libtcodpy .RIGHT ,
1535
1460
)
0 commit comments