Advertisement
Guest User

Untitled

a guest
Jul 30th, 2015
189
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 6.01 KB | None | 0 0
  1. # dungeon.rb Version 1.0
  2. # Elliot Temple
  3. # May 31, 2006
  4. #
  5. # This is my first Ruby Quiz entry
  6. #
  7. # For Ruby Quiz #80
  8. # http://rubyquiz.com/quiz80.html
  9. #
  10. # Generates an ASCII dungeon with an evolutionary algorithm. Makes random
  11. # changes and calls undo if the evaluate method returns a lower number.
  12. # Continues for a while, then makes sure to get valid output.
  13. #
  14. # It works but could benefit from tuning various numbers and some new features
  15. # in the evaluate function. It could also be faster. Sample output at bottom.
  16.  
  17. class Tile
  18.   attr_accessor :x, :y
  19.   @@TileType = Struct.new(:graphic, :frequency, :walkable)
  20.   @@data = {
  21.   :wall => @@TileType.new("#", 250, false),
  22.   :open => @@TileType.new(" ", 120, true),
  23.   :water => @@TileType.new("~", 10, true),
  24.   :stairs_up => @@TileType.new("<", 1, true),
  25.   :stairs_down => @@TileType.new(">", 1, true)
  26.   }
  27.   def initialize x, y, type = :any
  28.     @x = x
  29.     @y = y
  30.     if type == :any
  31.       @type_history = [get_rand_tile]
  32.     else
  33.       @type_history = [type]
  34.     end
  35.   end
  36.   def type
  37.     @type_history.last
  38.   end
  39.   def to_s
  40.     @@data[@type_history.last].graphic
  41.   end
  42.   def get_rand_tile
  43.     total = @@data.inject(0) { |total, pair| total + pair[1].frequency }
  44.     @@data.each do |k,v|
  45.       return k if rand(total) < v.frequency
  46.       total -= v.frequency
  47.     end
  48.   end
  49.   def random_change
  50.     @type_history << get_rand_tile
  51.   end
  52.   def undo(n=1)
  53.     n.times do
  54.       @type_history.pop
  55.     end
  56.   end
  57.   def walkable?
  58.     @@data[@type_history.last].walkable
  59.   end
  60. end
  61.  
  62. class Map
  63.   def initialize(height,width)
  64.     @height = height
  65.     @width = width
  66.     @map = []
  67.     @changeable_tiles = []
  68.     @last_changed = []
  69.     width.times do |i|
  70.       column = []
  71.       height.times do |j|
  72.         if (j == 0) or (j == width - 1) or (i == 0) or (i == width - 1)
  73.           column << Tile.new(i, j, :wall)
  74.         else
  75.           tmp = Tile.new(i, j)
  76.           column << tmp
  77.           @changeable_tiles << tmp
  78.         end
  79.       end
  80.       @map << column
  81.     end
  82.     @changeable_tiles = @changeable_tiles.sort_by {rand}
  83.     # x = @changeable_tiles.shift
  84.     # x.become_stairs_up
  85.     # x = @changeable_tiles.shift
  86.     # x.become_stairs_down
  87.   end
  88.   def to_s
  89.     # old version that put # around the output
  90.     # '#' * (@width+2) + "\n" + (@map.collect { |row| '#' + row.collect {|tile| tile.to_s}.join("") + '#' }.join "\n") + "\n" + '#' * (@width+2)
  91.     @map.collect { |row| row.collect {|tile| tile.to_s}.join("") }.join "\n"
  92.   end
  93.  
  94.   def update n=1
  95.     n.times do
  96.       x = @changeable_tiles[rand(@changeable_tiles.length)]
  97.       x.random_change
  98.       @last_changed << x
  99.     end
  100.   end
  101.  
  102.   def undo n=1
  103.     n.times do
  104.       @last_changed.pop.undo
  105.     end
  106.   end
  107.  
  108.   def path_between start, destination, exclude = []
  109.     return false if start.nil?
  110.     return true if start == destination
  111.     return false unless start.walkable?
  112.     return false if exclude.include?(start)
  113.     exclude << start
  114.     path_between(self.down(start), destination, exclude) or path_between(self.up(start), destination, exclude) or path_between(self.left(start), destination, exclude) or path_between(self.right(start), destination, exclude)
  115.   end
  116.  
  117.   def path_between2 start, destination
  118.     g = find_group(start)
  119.     g.include?(destination)
  120.   end
  121.    
  122.   def find_group start, walkable = true, group = []
  123.     return group if start.nil?
  124.     return group unless start.walkable? == walkable
  125.     return group if group.include?(start)
  126.     group << start
  127.     find_group(self.down(start), walkable, group)
  128.     find_group(self.up(start), walkable, group)
  129.     find_group(self.left(start), walkable, group)
  130.     find_group(self.right(start), walkable, group)
  131.     return group
  132.   end
  133.  
  134.   def count_groups walkable = true
  135.     tiles = @map.flatten.select { |tile| tile.walkable? == walkable }
  136.     count = 0
  137.     while tiles.any?
  138.       count += 1
  139.       tiles -= find_group(tiles[0], walkable)
  140.     end
  141.     count
  142.   end
  143.  
  144.   def left tile
  145.     @map[tile.x - 1][tile.y] rescue nil
  146.   end  
  147.   def right tile
  148.     @map[tile.x + 1][tile.y] rescue nil
  149.   end  
  150.   def down tile
  151.     @map[tile.x][tile.y - 1] rescue nil
  152.   end  
  153.   def up tile
  154.     @map[tile.x][tile.y + 1] rescue nil
  155.   end
  156.    
  157.   def stair_distance
  158.     (find_one(:stairs_up).x - find_one(:stairs_down).x).abs + (find_one(:stairs_up).y - find_one(:stairs_down).y).abs
  159.   end
  160.  
  161.   def find_one tile_type
  162.     @map.flatten.detect {|tile| tile_type == tile.type}
  163.   end
  164.  
  165.   def number_of tile_type
  166.     @map.flatten.inject(0) do |total, tile|
  167.       tile.type == tile_type ? total + 1 : total
  168.     end
  169.   end
  170.  
  171.   def valid?
  172.     return false unless number_of(:stairs_up) == 1
  173.     return false unless number_of(:stairs_down) == 1
  174.     return false unless path_between(find_one(:stairs_up), find_one(:stairs_down))        
  175.     true
  176.   end
  177.  
  178.   def evaluate
  179.     score = 0
  180.     score -= 200 unless valid?
  181.     if (number_of(:stairs_up) == 1) && (number_of(:stairs_down) == 1)
  182.       score += 200 * stair_distance
  183.     end
  184.     score -= 100 * count_groups(true)
  185.     score -= 70 * count_groups(false)
  186.   end
  187. end
  188.  
  189. map = Map.new 15,15
  190.  
  191. tmp = map.to_s
  192. map.update 50
  193. map.undo 40
  194. map.update 50
  195. map.undo 60
  196. raise "undo bug" unless tmp == map.to_s
  197.  
  198. valid_steps = 0
  199. e = map.evaluate
  200. n = 25
  201. undos = 0
  202.  
  203. 2000.times do
  204.   map.update n
  205.   if map.evaluate >= e
  206.     e = map.evaluate
  207.   else
  208.     map.undo n
  209.     undos += 1
  210.   end
  211. end
  212.  
  213. until map.valid?
  214.   map.update 1
  215.   valid_steps += 1
  216. end
  217.  
  218. puts map
  219. puts "steps to validate #{valid_steps}"
  220. puts "undos #{undos}"
  221. puts "stair distance is #{map.stair_distance}"
  222. puts map.count_groups
  223. puts map.count_groups(false)
  224. puts map.evaluate
  225.  
  226. =begin
  227. Sample Output:
  228.  
  229. ###############
  230. ## #####   ## #
  231. ## ## ## ### ##
  232. #  #         ##
  233. ## > ## #######
  234. ##  ##  #######
  235. ####### ## # ##
  236. ### ### ##    #
  237. ###  ##     # #
  238. #   #### #~~###
  239. ### #### ##  ##
  240. ####~     #####
  241. ##### #  #### #
  242. #    <## ######
  243. ###############
  244. steps to validate 0
  245. undos 1959
  246. stair distance is 11
  247. 4
  248. 1
  249. 1730
  250. =end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement