From 222b28d196dbd33abd6204f2b97330492dfb4c9f Mon Sep 17 00:00:00 2001 From: Daanish Date: Fri, 3 Apr 2026 02:51:11 +0100 Subject: [PATCH] Add new assets and implement minimap functionality - Added new image assets for game over screens: `game_over_dead_bg.png`, `game_over_victory_bg.png`, and their respective Aseprite files. - Introduced a new `Minimap` class to manage torch placement and rendering on the minimap. - Implemented methods for adding and removing torches, updating the grid area, and drawing the minimap based on the current state of the game. - Enhanced the minimap display with color coding for walls, empty spaces, and torches. --- assets/images/direction.aseprite | Bin 0 -> 388 bytes assets/images/direction.png | Bin 0 -> 162 bytes assets/images/game_over_bg.aseprite | Bin 5976 -> 0 bytes assets/images/game_over_bg.png | Bin 2908 -> 0 bytes assets/images/game_over_dead_bg.aseprite | Bin 0 -> 6077 bytes assets/images/game_over_dead_bg.png | Bin 0 -> 2924 bytes assets/images/game_over_victory_bg.aseprite | Bin 0 -> 5621 bytes assets/images/game_over_victory_bg.png | Bin 0 -> 4466 bytes assets/images/heart.aseprite | Bin 0 -> 408 bytes assets/images/heart.png | Bin 0 -> 187 bytes assets/images/memory.aseprite | Bin 0 -> 517 bytes assets/images/memory.png | Bin 0 -> 186 bytes game/character.rb | 6 +- game/hud/health.rb | 9 +- game/hud/layer.rb | 17 ++++ game/hud/minimap.rb | 92 ++++++++++++++++++++ game/props/handler.rb | 31 ++++++- game/props/torch.rb | 9 ++ game/scene.rb | 4 + game/weapons/blade_of_recursion.rb | 9 +- game_over/scene.rb | 2 +- settings/scene.rb | 2 +- todo.md | 16 ---- utils/maze.rb | 44 ++++++++-- 24 files changed, 209 insertions(+), 32 deletions(-) create mode 100644 assets/images/direction.aseprite create mode 100644 assets/images/direction.png delete mode 100644 assets/images/game_over_bg.aseprite delete mode 100644 assets/images/game_over_bg.png create mode 100644 assets/images/game_over_dead_bg.aseprite create mode 100644 assets/images/game_over_dead_bg.png create mode 100644 assets/images/game_over_victory_bg.aseprite create mode 100644 assets/images/game_over_victory_bg.png create mode 100644 assets/images/heart.aseprite create mode 100644 assets/images/heart.png create mode 100644 assets/images/memory.aseprite create mode 100644 assets/images/memory.png create mode 100644 game/hud/minimap.rb delete mode 100644 todo.md diff --git a/assets/images/direction.aseprite b/assets/images/direction.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..bc771d8e696f56b120270b69f983aab50f0ef885 GIT binary patch literal 388 zcmZo+WMFu(l#zjtfsa9ffsuiMA%y`MC@?UB1Q`Sv1dxTP#%5t;VEFZs1#Bw|0|SE? z0|NuQ0@z9!?S*WGMo7{AZwq2 z`IJ2GO#L8l6(bu(#sZr+anf8_0ufQeZ~1`#;D<3_gjKsYMEgp$rTRtO^VaObiSR|3L&;AtwVL zL&cor1P8|d6AjE5rtOYj(dI21BiDBE%E71kdLGQlIV2JT_ zaSYLzn4EHe@1OEU$&H!`hnBvqTPS7bTrjI`U8m#5|6*ZVdv2Kgy;Pn62D>X5mZym< zSGvt|xkaz^&j0`b literal 0 HcmV?d00001 diff --git a/assets/images/game_over_bg.aseprite b/assets/images/game_over_bg.aseprite deleted file mode 100644 index ee6070fd48149e661bc8fc8b79fcf170dacd2eb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5976 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 diff --git a/assets/images/game_over_dead_bg.aseprite b/assets/images/game_over_dead_bg.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..f7597e839f26f64c2f83b93cedcb1219352649e6 GIT binary patch literal 6077 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 literal 0 HcmV?d00001 diff --git a/assets/images/game_over_dead_bg.png b/assets/images/game_over_dead_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6b52e08349adddaf141016f80c40424899d81cb0 GIT binary patch literal 2924 zcmeAS@N?(olHy`uVBq!ia0y~yV7kD-!1#cJje&uo=V;U#1_lPk;vjb?hIQv;UNSIn zt9iOOhE&XXd-q`OYc~;xK+6F3S$v@#AG<$Xa$TYS?ZFzmCWY=FpZZ<9Y|`^zot>~r zpN;KD+3vgh^cWZpZ2zI5HR*IEIe0gQc$-&t*hTTX|hN*=O&)dCzCt-o7{Y<-Ity^XAptZhrfxb9?{a z8=t?w5uNig|H8ff+ua|r(9ru2c70{ou=Mxe+E-ug?CX8^%GF$dlU@mT-kGoG{@pvb zPrTav_q};1Z|)<_HBH-Zzx}rLt3dkZ@8+y;EBD!+FEhL^Kil&Dz55GFmsJ0zzkS9ob z!RyVd?HKy9dp3UM_)tQMQ$MI3;DzY==M+YwN?A#cMoTk zuHE|mZ^Ul#>}#c0&R40ue$+9C`~qcNH2;CZ9>Z;N58c;9Uf-l+Z*&Pew$FaMt3vmD4PT<#`FHzo@@rASx23E)3;AmiJH%6U|?YIboFyt=akR{0A8nsM*si- literal 0 HcmV?d00001 diff --git a/assets/images/game_over_victory_bg.aseprite b/assets/images/game_over_victory_bg.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..965c05df496e9c0685e25cc5fddfc0e8aef96203 GIT binary patch literal 5621 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 literal 0 HcmV?d00001 diff --git a/assets/images/game_over_victory_bg.png b/assets/images/game_over_victory_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..bcdaed4f687124d64fe7dfc8e51b48e698fe7731 GIT binary patch literal 4466 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 literal 0 HcmV?d00001 diff --git a/assets/images/heart.png b/assets/images/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..c94689083344f47f4a7997ab153b721fa9856f14 GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0y~yVBiB`4mJh`26+$Niwq15jKx9jP7LeL$-HD>U?}x; zaSYLzxcAaV(FO$`mJ8gwBce{|^u5%Hh_LcmBP8te8`oE>VvD6t+4z zJNwdZzi+XI{@e@?EXr-K#B2O^6|-d$Q1aZ!-R&0mgVi?f59_kWCz6gUN+uacf47d? n=qkul*l+ZrpvCRsgU8u=JFXf_J$6b5xrV{h)z4*}Q$iB}-iATV literal 0 HcmV?d00001 diff --git a/assets/images/memory.aseprite b/assets/images/memory.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..3994b93384c852d64661a98ac1e61f8c87d484f7 GIT binary patch literal 517 zcmZQ&Vqkc%l#xMzL4ZMlfsuiMA%y`MC^9gD1i|vi;?!cdGBPmy`p5#dm4$(UL5zWc zfn5P?C6W~n7#J8N6%?T&3Q!tO{byiMQZV_?z~HK3^Phns&C>8c14Dn9^?wG2`-N)% z85nLZNc+#g@b2)e{|pSjKbie!V3_$d;6DRH);@#(3=HN|^8Pb0cxUSWXJ82OR{77s zU}R(YpMk;NzT`gxgF!;me+Gu=yodi87?NlI|Ife>fA-0L28PqO|NLiQ`2QayGJEaz z{|pQ*b?yHd7&5c6{xdK{h6Vj+U?_9y`p>|y#>fcdhVbP73=G$+cY)kC-RnOCLsve? zc91XR85kH?6qrFwDEBVYgDpZqH#v*_yI$d#>A)m&wLck44ofy`glX=O&z)<4p z;uvDlo1Cyfz+e&goSF~c?G=5Wah;L)6~HsWxbW})?@R2$r0z1QeUkM*(D0wXE_rFE zSwlpFkc7JYpTF`_j~c@dzcRHBV47H#=wR01`gpbf|3CwuIomFJ_$Vx57K;)3e*f>{ oGYmzopr02Q)DKL7v# literal 0 HcmV?d00001 diff --git a/game/character.rb b/game/character.rb index 612e629..c31be6a 100644 --- a/game/character.rb +++ b/game/character.rb @@ -126,11 +126,11 @@ class Character $bus.emit(:trap_stepped_on, rect, :character) end - # teleport to boss room for debug purposes + # teleport to exit room for debug purposes if $bus.get(:settings, :debug) && Gosu.button_down?(Gosu::KB_T) - room_coords = $bus.get(:boss_room_coords) + room_coords = $bus.get(:exit_room_coords) if room_coords - @world_x, @world_y = [room_coords[0] * 60 + 5, room_coords[1] * 60 + 5] + @world_x, @world_y = [room_coords[0] * 120 + 90, room_coords[1] * 120 + 90] end end diff --git a/game/hud/health.rb b/game/hud/health.rb index b9a476f..a1e81de 100644 --- a/game/hud/health.rb +++ b/game/hud/health.rb @@ -5,6 +5,8 @@ class HealthBar @max_health = max_health @health = max_health + @heart_image = Gosu::Image.new("assets/images/heart.png", retro: true) + $bus.on(:blast) do |x, y, radius, damage, safety| next if safety == :safe @@ -21,6 +23,7 @@ class HealthBar @health -= amount if @health <= 0 @health = 0 + $is_dead = true $bus.emit(:change_scene, GameOver) end end @@ -32,10 +35,12 @@ class HealthBar def draw # Draw the background of the health bar (red) - Gosu.draw_rect(30, 10, 100, 10, Gosu::Color::RED) + Gosu.draw_rect(SCREEN_SIZE[0] - 100 - 10, 180, 100, 10, Gosu::Color::RED) # Draw the foreground of the health bar (green) based on current health health_width = (health.to_f / max_health) * 100 - Gosu.draw_rect(30, 10, health_width, 10, Gosu::Color::GREEN) + Gosu.draw_rect(SCREEN_SIZE[0] - 100 - 10, 180, health_width, 10, Gosu::Color::GREEN) + + @heart_image.draw(SCREEN_SIZE[0] - 100 - 20, 173, 1, 1.5, 1.5) end end \ No newline at end of file diff --git a/game/hud/layer.rb b/game/hud/layer.rb index e0b22d0..21ef28d 100644 --- a/game/hud/layer.rb +++ b/game/hud/layer.rb @@ -1,6 +1,7 @@ require_relative "inventory" require_relative "health" require_relative "logs" +require_relative "minimap" class HUDLayer def initialize @@ -9,6 +10,9 @@ class HUDLayer @inventory = Inventory.new @health = HealthBar.new(100) @logs = GameLogger.new + @minimap = Minimap.new + + @direction_sprite = Gosu::Image.new("assets/images/direction.png", retro: true) $bus.on(:enemy_attack) do |damage| @health.take_damage(damage) @@ -26,6 +30,19 @@ class HUDLayer @health.draw @logs.draw + player_x, player_y = $bus.get(:player_position) || [0, 0] + tile_x = ((player_x - 90) / 120).round + tile_y = ((player_y - 90) / 120).round + center_x = tile_x * 2 + 1 + center_y = tile_y * 2 + 1 + @minimap.draw(center_x, center_y) + + if $bus.get(:radar_triangulation?, player_x, player_y) + exit_x, exit_y = $bus.get(:exit_room_coords) || [0, 0] + angle_to_exit = Math.atan2(exit_y - tile_x, exit_x - tile_y) * 180 / Math::PI + @direction_sprite.draw_rot(SCREEN_SIZE[0] / 2, SCREEN_SIZE[1] / 2, Float::INFINITY, angle_to_exit, 0.5, 0.5, 5, 5) + end + @hud_fg.draw(0, 0, Float::INFINITY) end diff --git a/game/hud/minimap.rb b/game/hud/minimap.rb new file mode 100644 index 0000000..14dd904 --- /dev/null +++ b/game/hud/minimap.rb @@ -0,0 +1,92 @@ +class Minimap + CELL_SIZE = 3 # pixels per minimap cell + RADIUS = 10 # cells around torch to update + + # Please ignore my overuse of that stupid ruby conditional assignment operator + + def initialize + @torches = [] + @grid_cache = {} + + $bus.on(:torch_placed) do |gx, gy| + add_torch(gx, gy) + end + + $bus.on(:torch_removed) do |gx, gy| + remove_torch(gx, gy) + end + end + + def add_torch(x, y) + x, y = x * 2 + 1, y * 2 + 1 # convert from room coords to grid coords + @torches << [x, y] + update_grid_area(x, y) + end + + def remove_torch(x, y) + x, y = x * 2 + 1, y * 2 + 1 + @torches.delete([x, y]) + update_grid_area(x, y) + end + + # Update only cells in 10-cell box around given gx,gy + def update_grid_area(cx, cy) + (cx-RADIUS..cx+RADIUS).each do |gx| + (cy-RADIUS..cy+RADIUS).each do |gy| + if @torches.any? { |tx, ty| (gx - tx).abs <= RADIUS && (gy - ty).abs <= RADIUS } + @grid_cache[[gx, gy]] = if @torches.include?([gx, gy]) + :torch + elsif $bus.get(:maze_wall?, gx, gy) + :wall + else + :empty + end + else + @grid_cache.delete([gx, gy]) + end + end + end + end + + def draw(center_x, center_y) + half_size = 25 # 40 cells → 20 each side + map_max = $bus.get(:maze_size).map { |s| s * 2 - 1 } || 0 + + offset_x = SCREEN_SIZE[0] - CELL_SIZE * (half_size * 2 + 1) - 10 + offset_y = 10 + + size_in_cells = 2 * half_size + 1 + width = size_in_cells * CELL_SIZE + height = size_in_cells * CELL_SIZE + + # border + Gosu.draw_rect(offset_x - 3, offset_y - 3, width + 6, height + 6, Gosu::Color.new(0xFFa9b2a2), Float::INFINITY) + + (-half_size..half_size).each do |dx| + (-half_size..half_size).each do |dy| + gx = center_x + dx + gy = center_y + dy + + type = if gx < 0 || gy < 0 || gx > map_max[0] || gy > map_max[1] + :wall + else + @grid_cache[[gx, gy]] || :unrevealed + end + + color = case type + when :wall then Gosu::Color.new(0xFF28353e) + when :empty then Gosu::Color.new(0xFFd6dad3) + when :torch then Gosu::Color.new(0xFFfbb954) + else Gosu::Color.new(0xFF576069) + end + + color = Gosu::Color::RED if gx == center_x && gy == center_y + + x = (dx + half_size) * CELL_SIZE + offset_x + y = (dy + half_size) * CELL_SIZE + offset_y + + Gosu.draw_rect(x, y, CELL_SIZE, CELL_SIZE, color, Float::INFINITY) + end + end + end +end \ No newline at end of file diff --git a/game/props/handler.rb b/game/props/handler.rb index eb4c2e5..e7e4cb8 100644 --- a/game/props/handler.rb +++ b/game/props/handler.rb @@ -22,6 +22,34 @@ class PropsHandler $bus.on(:nearby_torches) do |rect| nearby_props(rect).select { |p| p.is_a?(Torch) }.map(&:rect).map { |t| [t[0] + t[2] / 2, t[1] + t[3] / 2] } end + + $bus.on(:radar_triangulation?) do |player_x, player_y| + radar_triangulation?(player_x, player_y) + end + end + + def radar_triangulation?(player_x, player_y) + # small rect around player to find the first radar + small_rect = [player_x - 60, player_y - 60, 120, 120] # 1 block radius + first_radar = nearby_props(small_rect).find { |p| p.is_a?(Radar) } + return false unless first_radar + + # larger rect to find other radars (max 15 blocks away, 15*120 pixels) + large_rect = [first_radar.x * 120 - 15 * 120, first_radar.y * 120 - 15 * 120, 30 * 120, 30 * 120] + nearby_radars = nearby_props(large_rect).select { |p| p.is_a?(Radar) } + + # need at least 3 radars total + return false if nearby_radars.size < 3 + + nearby_radars.combination(3).any? do |r1, r2, r3| + next unless r1 == first_radar || r2 == first_radar || r3 == first_radar + + d1 = Math.hypot((r1.x - r2.x), (r1.y - r2.y)) + d2 = Math.hypot((r1.x - r3.x), (r1.y - r3.y)) + d3 = Math.hypot((r2.x - r3.x), (r2.y - r3.y)) + + [d1, d2, d3].all? { |d| d >= 3 && d <= 15 } + end end def spawn_chests! @@ -49,7 +77,7 @@ class PropsHandler end end - # Add one in the start room for players to have something to interact with right away + # Add one in the start room for players to have something right away start_room_x, start_room_y = $bus.get(:start_room_coords) || [0, 0] x, y = [ [start_room_x, start_room_y], @@ -103,6 +131,7 @@ class PropsHandler return if @grid[[tile[0], tile[1]]] # can't place if something is already there return if $bus.get_all(:collides?, torch.collision_rect).include?(:character) # can't place if colliding with player return unless $bus.get(:consume, :torch, 1) + $bus.emit(:torch_placed, torch.x, torch.y) add(torch) elsif selected_item == :radar player_x, player_y = $bus.get(:player_position) || [0, 0] diff --git a/game/props/torch.rb b/game/props/torch.rb index 3aeeeda..aa7a5ac 100644 --- a/game/props/torch.rb +++ b/game/props/torch.rb @@ -6,4 +6,13 @@ class Torch < Prop def resources { wood: rand(0..8) } end + + def update(dt) + dead = super(dt) + if dead + $bus.emit(:torch_removed, @x, @y) + pp "Emitted torch_removed for torch at #{@x}, #{@y}" + end + dead + end end \ No newline at end of file diff --git a/game/scene.rb b/game/scene.rb index d772d34..e92ecb0 100644 --- a/game/scene.rb +++ b/game/scene.rb @@ -9,6 +9,8 @@ require_relative 'character' class Game < Scene def initialize super + $is_dead = false + @font = Gosu::Font.new(24) @floor_image = Gosu::Image.new("assets/images/floor.png", retro: true) @@ -94,6 +96,8 @@ class Game < Scene end def button_down(id, pos) + # Pressing ESC returns to the menu, this is bad but I don't have time to make a proper pause menu, + # as the game has no persistent state outside of the current scene, just returning to the menu resets everything return $bus.emit(:change_scene, Menu.new) if id == Gosu::KB_ESCAPE return if @hud.button_down(id, pos) diff --git a/game/weapons/blade_of_recursion.rb b/game/weapons/blade_of_recursion.rb index 1e096af..77dfa1d 100644 --- a/game/weapons/blade_of_recursion.rb +++ b/game/weapons/blade_of_recursion.rb @@ -11,6 +11,8 @@ class BladeOfRecursion # the knife is pointed top-right @sprite = Gosu::Image.new('assets/images/blade_of_recursion.png', retro: true) + + @memory_sprite = Gosu::Image.new('assets/images/memory.png', retro: true) end def attack(direction) @@ -72,10 +74,9 @@ class BladeOfRecursion # this should be in the HUD layer but due to time constraints im putting it here, sorry bar_width = 100 bar_height = 10 - padding = 10 - bar_x = SCREEN_SIZE[0] - bar_width - padding - bar_y = padding + bar_x = SCREEN_SIZE[0] - bar_width - 10 + bar_y = 205 # full red background Gosu.draw_rect(bar_x, bar_y, bar_width, bar_height, Gosu::Color::RED, Float::INFINITY) @@ -84,5 +85,7 @@ class BladeOfRecursion damage_ratio = Math.log2(@damage) / 9.0 damage_width = bar_width * [damage_ratio, 1.0].min Gosu.draw_rect(bar_x, bar_y, damage_width, bar_height, Gosu::Color::GREEN, Float::INFINITY) + + @memory_sprite.draw(bar_x - 10, bar_y - 7, Float::INFINITY, 1.5, 1.5) end end \ No newline at end of file diff --git a/game_over/scene.rb b/game_over/scene.rb index 727015a..cb9eed0 100644 --- a/game_over/scene.rb +++ b/game_over/scene.rb @@ -8,7 +8,7 @@ class GameOver < Scene super @font = Gosu::Font.new(32, name: "assets/fonts/tn.ttf") @selected_index = 0 - @bg_image = Gosu::Image.new("assets/images/game_over_bg.png", retro: true) + @bg_image = Gosu::Image.new("assets/images/game_over_#{$is_dead ? 'dead' : 'victory'}_bg.png", retro: true) @last_mouse_pos = nil end diff --git a/settings/scene.rb b/settings/scene.rb index bca37e1..107a23b 100644 --- a/settings/scene.rb +++ b/settings/scene.rb @@ -10,6 +10,6 @@ class Settings < Scene end def button_down(id, _pos) - $bus.emit(:change_scene, Menu.new) if id == Gosu::KB_ESCAPE + $bus.emit(:change_scene, Menu) if id == Gosu::KB_ESCAPE end end \ No newline at end of file diff --git a/todo.md b/todo.md deleted file mode 100644 index 2a29855..0000000 --- a/todo.md +++ /dev/null @@ -1,16 +0,0 @@ -So, - -#landmines and event horizon should be easy - done - -#make enemy spawn in the boss rooms with randomness + enemy count - done -#make the lizard kind of enemy with very high health + dps but slow - done - -#make blade work as planned - done -#make bow quantum - done - -make minimap for the torches revealing teh maze -click on minimap makes game pause to allow pausing for safety - -make radars find exit if even one placed, and make it expensive - -then start writing every mistake and time terribility \ No newline at end of file diff --git a/utils/maze.rb b/utils/maze.rb index 271d333..a6b8aac 100644 --- a/utils/maze.rb +++ b/utils/maze.rb @@ -19,8 +19,12 @@ class MazeData next @start_room end + $bus.on(:exit_room_coords) do + next @exit_room + end + $bus.on(:room?) do |gx, gy| - ([@start_room] + @boss_rooms).any? do |rx, ry| + ([@start_room, @exit_room] + @boss_rooms).any? do |rx, ry| gx.between?(rx, rx + BOSS_ROOM_SIZE) && gy.between?(ry, ry + BOSS_ROOM_SIZE) end @@ -30,6 +34,18 @@ class MazeData next @boss_rooms end + $bus.on(:maze_wall?) do |gx, gy| + next wall_at?(gx, gy) + end + + $bus.on(:torch_placed) do |gx, gy| + # check for end condition, if torch is placed in exit room, change scene to victory + if (gx.between?(@exit_room[0], @exit_room[0] + BOSS_ROOM_SIZE - 1) && + gy.between?(@exit_room[1], @exit_room[1] + BOSS_ROOM_SIZE - 1)) + $bus.emit(:change_scene, GameOver) + end + end + return unless $bus.get(:settings, :debug) print_debug @@ -113,7 +129,7 @@ class MazeData def wall_at?(gx, gy) return true if gx == 0 || gy == 0 || gx == width - 1 || gy == height - 1 - @boss_rooms.each do |rx, ry| + (@boss_rooms + [@start_room, @exit_room]).each do |rx, ry| display_x1 = rx * 2 + 1 display_y1 = ry * 2 + 1 display_x2 = display_x1 + (BOSS_ROOM_SIZE - 1) * 2 @@ -184,7 +200,7 @@ class MazeData end def fix_room_entrances! - (@boss_rooms + [@start_room]).each do |x, y| + (@boss_rooms + [@start_room, @exit_room]).each do |x, y| dir = [N, S, E, W].sample cx = x + BOSS_ROOM_SIZE / 2 cy = y + BOSS_ROOM_SIZE / 2 @@ -239,8 +255,26 @@ class MazeData rooms << [x, y] end - @start_room = rooms.shift - @boss_rooms = rooms + # assign the start and exit room to be the two furthest apart rooms + @start_room, @exit_room = furthest_pair(rooms) + @boss_rooms = rooms - [@start_room, @exit_room] + end + + def furthest_pair(rects) + max_dist = -Float::INFINITY + pair = nil + + rects.combination(2) do |r1, r2| + x1, y1 = r1 + x2, y2 = r2 + dist = Math.hypot(x2 - x1, y2 - y1) + if dist > max_dist + max_dist = dist + pair = [r1, r2] + end + end + + pair end def carve_passages_from(cx, cy)