Advertisement
Guest User

Untitled

a guest
Jan 17th, 2012
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 12.17 KB | None | 0 0
  1. #Needed for key input
  2. require 'Win32API'
  3.  
  4. #Node Class represents a Node in our Graph/Array
  5. class Node
  6.     def initialize(x, y)
  7.         @x = x
  8.         @y = y
  9.     end
  10.    
  11.     def x=(val)
  12.         @x = val
  13.     end
  14.    
  15.     def y=(val)
  16.         @y = val
  17.     end
  18.    
  19.     def x()
  20.         @x
  21.     end
  22.  
  23.     def y()
  24.         @y
  25.     end
  26.    
  27.     def to_s()
  28.         return "x: " + @x.to_s + ", y: " + @y.to_s
  29.     end
  30. end
  31.  
  32. #Maze Class creates a Labyrinth as playfield
  33. class Maze
  34.     #initialize the class with dimension
  35.     #Only odd numbers are valid in order for the algorithm to work properly
  36.     def initialize(rows, cols)
  37.         @rows = rows
  38.         @cols = cols
  39.        
  40.         #Makes sure the starting values are odd numbers and within range
  41.         @oddRowNums = ((rows-1)/2).times.map{ |i| i*2+1 }
  42.         @oddColNums = ((cols-1)/2).times.map{ |i| i*2+1 }
  43.         @sY = @oddRowNums[rand(@oddRowNums.length())]
  44.         @sX = @oddColNums[rand(@oddColNums.length())]
  45.        
  46.         initField()
  47.     end
  48.    
  49.     def initField()
  50.         #Represents the maze field
  51.         @field = Array.new(rows){ Array.new(cols) }
  52.         #List where we save all frontier nodes
  53.         @frontierList = Array.new()
  54.        
  55.         #Init field full of walls represented by the value 5
  56.         for i in 0..rows-1
  57.             for j in 0..cols-1
  58.                 @field[i][j] = 5
  59.             end
  60.         end
  61.     end
  62.    
  63.     def generate()
  64.         #Mark the start
  65.         markAdjacent(@sX, @sY)
  66.         while(!(@frontierList.empty?))
  67.             system('cls')
  68.             curNode = @frontierList.delete_at(rand(@frontierList.length()))
  69.             nList = neighbours(curNode.x, curNode.y)
  70.  
  71.             rndNeighbour = nList[rand(nList.length)]
  72.             carve(rndNeighbour, curNode)
  73.            
  74.             markAdjacent(curNode.x, curNode.y)
  75.            
  76.             printField()
  77.             sleep(0.1)
  78.         end
  79.        
  80.         #Place exit
  81.         makeExit()
  82.         #Place start
  83.         startNode = placeStart()
  84.         system('cls')
  85.        
  86.         return startNode
  87.     end
  88.    
  89.     def placeStart()
  90.         #Find a random valid starting position for the player
  91.         while(true)
  92.             x = @oddColNums[rand(@oddColNums.length())]
  93.             y = @oddRowNums[rand(@oddRowNums.length())]
  94.            
  95.             if(@field[y][x] != 5)
  96.                 distanceX = @eX - x
  97.                 distanceY = @eY - y
  98.                
  99.                 #Make sure the start isn't too close to the exit
  100.                 if(distanceX.abs > ((cols-1)/2).to_i) || (distanceY.abs > ((rows-1)/2).to_i)
  101.                     @field[y][x] = 1
  102.                     break
  103.                 end
  104.             end
  105.         end
  106.        
  107.         return Node.new(x,y)
  108.     end
  109.    
  110.     def makeExit()
  111.         #####
  112.         # This is a Diagram depicting the possible sides
  113.         # and values for x,y (active = 1, inactive = 0)
  114.         # for the exit
  115.         #
  116.         #         1(1,0)
  117.         #       #########
  118.         # 1(0,1)#       # 0(0,1)
  119.         #       #########
  120.         #         0(1,0)
  121.         #
  122.        
  123.         #Choose a random side and choose which coordinate is the active one
  124.         side = rand(2)
  125.         xSet = rand(2)
  126.         ySet = 1-xSet
  127.         @eX = 0
  128.         @eY = 0
  129.         inValid = true
  130.        
  131.         #Run as long as we have found a proper value where the opposite neighbour
  132.         #is not a wall so we can actually exit
  133.         while(inValid)
  134.             #These will keep track of which neighbour to lookup
  135.             valX = 0
  136.             valY = 0
  137.            
  138.             #Check for side 1 as shown in diagram
  139.             if(side == 1)
  140.                 if(xSet == 1)   #1(1,0)
  141.                     #x can be random between 1 and cols-2 so that we cant even select
  142.                     #the corners of the field as an exit
  143.                     @eX = 1+rand(cols-2)
  144.                     @eY = 0
  145.                 else            #1(0,1)
  146.                     #this time y is random and x is fixed
  147.                     @eX = 0
  148.                     @eY = 1+rand(rows-2)
  149.                 end
  150.                
  151.                 if(@eX == 0)
  152.                     #In case x was fixed, look at the neighbour to the right of it
  153.                     valX = 1
  154.                 else
  155.                     #In case y was fixed, look at the neighbour below it
  156.                     valY = 1
  157.                 end
  158.             else
  159.                 if(xSet == 1)   #0(1,0)
  160.                     #Again, x can be random wheras y is fixed at maximum value (we are
  161.                     #on side 0 now, so fixed values are forced to maximum
  162.                     @eX = 1+rand(cols-2)
  163.                     @eY = rows-1
  164.                 else            #0(0,1)
  165.                     #y random, x fixed to maximum
  166.                     @eX = cols-1
  167.                     @eY = 1+rand(rows-2)
  168.                 end
  169.                
  170.                 if(@eX == cols-1)
  171.                     #If x was fixed, look up the neighbour to its left
  172.                     valX = -1
  173.                 else
  174.                     #If y was fixed, look up the neighbour above it
  175.                     valY = -1
  176.                 end
  177.             end
  178.            
  179.             #Actual neighbour lookup, if there's a wall as neighbour we wouldn't be able
  180.             #to exit so try with some new random values
  181.             if(@field[@eY+valY][@eX+valX] != 5)
  182.                 inValid = false
  183.             end
  184.         end
  185.        
  186.         #Sets the exit based on values found
  187.         @field[@eY][@eX] = 0
  188.     end
  189.    
  190.     #Marks the field at x,y and its frontiers
  191.     def markAdjacent(x, y)
  192.         @field[y][x] = 0
  193.         add_frontier(x-2, y)
  194.         add_frontier(x+2, y)
  195.         add_frontier(x, y-2)
  196.         add_frontier(x, y+2)
  197.     end
  198.  
  199.     #Adds the frontier if its within valid values and the frontier was not previously traversed
  200.     def add_frontier(x, y)
  201.         if (x > 0 && x < cols-1) && (y > 0 && y < rows-1) && (@field[y][x] == 5)
  202.             #Frontier is represented by the value 6
  203.             @field[y][x] = 6
  204.             @frontierList.push(Node.new(x, y))
  205.         end
  206.     end
  207.  
  208.     #Gets all neighbours of the node that have been marked as walkable
  209.     def neighbours(x, y)
  210.         list = Array.new()
  211.        
  212.         if(checkNode(x-2, y))
  213.             list.push(Node.new(x-2, y))
  214.         end
  215.        
  216.         if(checkNode(x+2, y))
  217.             list.push(Node.new(x+2, y))
  218.         end
  219.        
  220.         if(checkNode(x, y-2))
  221.             list.push(Node.new(x, y-2))
  222.         end
  223.        
  224.         if(checkNode(x, y+2))
  225.             list.push(Node.new(x, y+2))
  226.         end
  227.        
  228.         return list
  229.     end
  230.  
  231.     #Checks the given position if its walkable
  232.     def checkNode(x, y)
  233.         if (x > 0 && x < cols-1) && (y > 0 && y < rows-1) && (@field[y][x] == 0)
  234.             return true
  235.         end
  236.        
  237.         return false
  238.     end
  239.  
  240.     def carve(from, to)
  241.         if (from.y < to.y)
  242.             carveBlock(from.x, from.y+1)
  243.         end
  244.        
  245.         if (from.y > to.y)
  246.             carveBlock(from.x, from.y-1)
  247.         end
  248.        
  249.         if (from.x < to.x)
  250.             carveBlock(from.x+1, from.y)
  251.         end
  252.        
  253.         if (from.x > to.x)
  254.             carveBlock(from.x-1, from.y)
  255.         end
  256.     end
  257.  
  258.     def carveBlock(x, y)
  259.         if (x > 0 && x < cols-1) && (y > 0 && y < rows-1) && (@field[y][x] == 5)
  260.             @field[y][x] = 0
  261.         end
  262.     end
  263.    
  264.     #rows getter
  265.     def rows()
  266.         return @rows
  267.     end
  268.    
  269.     #cols getter
  270.     def cols()
  271.         return @cols
  272.     end
  273.    
  274.     #gets the field
  275.     def field()
  276.         return @field
  277.     end
  278.    
  279.     #Prints the field according to the internal representation of our playfield array
  280.     def printField()
  281.         for i in 0..rows-1
  282.             for j in 0..cols-1
  283.                 if @field[i][j] == 0
  284.                     #Walkable path
  285.                     print " "  
  286.                 elsif @field[i][j] == 6
  287.                     #Frontier
  288.                     print "F"
  289.                 elsif @field[i][j] == 5
  290.                     #Border
  291.                     print "#"
  292.                 end
  293.             end
  294.             puts
  295.         end
  296.         puts "Creating maze..."
  297.     end
  298. end
  299.  
  300.  
  301. #Game class represents our game. Stores the playing field and handles the character
  302. #move logic
  303. class Game
  304.  
  305.     #Initialize the game:
  306.     #rows and cols define the size of the playing field
  307.     #gChar is the visual representation of our character
  308.     #sX and sY are the starting X and Y coordinates - if they're wrong we just start at 1,1
  309.     def initialize(rows, cols, gChar, powerups, powerupRep)
  310.         @rows = rows
  311.         @cols = cols
  312.         @gChar = gChar
  313.         @running = true
  314.         if(powerups > (rows * cols)/2)
  315.             @powerups = cols-2
  316.         else
  317.             @powerups = powerups
  318.         end
  319.         @powerupRep = powerupRep
  320.         @collected = 0
  321.         @info
  322.     end
  323.    
  324.     #Sets up our playfield
  325.     def init()
  326.         #Create a maze playfield
  327.         maze = Maze.new(@rows, @cols)
  328.         #Set game character's node
  329.         @gNode = maze.generate()
  330.         #Fetch the field
  331.         @playfield = maze.field()
  332.         #Set starting positions based on what the field gave back as starting pos
  333.         #@gY = startNode.y
  334.         #@gX = startNode.x
  335.         setPowerups()
  336.     end
  337.    
  338.     #Sets randomly placed powerups into the gamefield
  339.     def setPowerups()
  340.         count = 0
  341.        
  342.         while (count < @powerups)
  343.             if(setPowerup() == true)
  344.                 count += 1
  345.             end
  346.         end
  347.     end
  348.    
  349.     #sets one powerup unit - returns true if success
  350.     def setPowerup()
  351.         #random x,y position
  352.         x = 1+rand(@cols-1)
  353.         y = 1+rand(@rows-1)
  354.        
  355.         #only place the powerup if the field is actually free (walkable)
  356.         if(@playfield[y][x] == 0)
  357.             @playfield[y][x] = 9
  358.            
  359.             return true
  360.         end
  361.        
  362.         return false
  363.     end
  364.    
  365.     #Updates the game
  366.     def update(keyinput)
  367.         @input = keyinput
  368.        
  369.         #Move character
  370.         moveChar(@input.chr)
  371.     end
  372.    
  373.     def winningScenario()
  374.         #If the player managed to walk the border he obviously hit the exit
  375.         if(((@gNode.x == 0) || (@gNode.y == 0) || (@gNode.x == @cols-1) || (@gNode.y == @rows-1)))
  376.             if(@collected.to_i() == @powerups.to_i())
  377.                 #Clear screen and print winning message - stop the game
  378.                 system('cls')
  379.                 puts
  380.                 puts
  381.                 puts
  382.                 puts "             #####################################"
  383.                 puts "             # Unlike this text, you made it out #"
  384.                 puts "             # of the crazy maze!                #"
  385.                 puts "             #          Congratulations!         #"
  386.                 puts "             #####################################"
  387.                 sleep(3)
  388.                 system('cls')
  389.                 @running = false
  390.              else
  391.                 @info = "Yea, you can't exit before collecting all powerups, bro"
  392.              end
  393.         end
  394.     end
  395.    
  396.     #Renders the game
  397.     def render()
  398.         printField()
  399.     end
  400.    
  401.     #Checks if we should keep the game loop alive
  402.     def isRunning()
  403.         #ESC = 27 stops the loop
  404.         if @input == 27
  405.             @running = false
  406.         end
  407.        
  408.         return @running
  409.     end
  410.    
  411.     #Prints the field according to the internal representation of our playfield array
  412.     def printField()
  413.         for i in 0..@rows-1
  414.             for j in 0..@cols-1
  415.                 if @playfield[i][j] == 0
  416.                     #Walkable path
  417.                     print " "  
  418.                 elsif @playfield[i][j] == 1
  419.                     #Character
  420.                     print @gChar
  421.                 elsif @playfield[i][j] == 5
  422.                     #Border
  423.                     print "#"
  424.                 elsif @playfield[i][j] == 9
  425.                     #Powerup
  426.                     print @powerupRep.to_s()
  427.                 end
  428.             end
  429.             puts
  430.         end
  431.        
  432.         puts "P: " + @collected.to_s()
  433.        
  434.         if(@info != "")
  435.             puts @info
  436.             @info = ""
  437.         end
  438.     end
  439.    
  440.     #This moves the cahracter around depending on the direction that was supplied W,S,A,D
  441.     def moveChar(dir)
  442.         case dir
  443.             #when "w" then move( 0,-1)
  444.             when "w" then move(Node.new(0, -1))
  445.             #when "s" then move( 0, 1)
  446.             when "s" then move(Node.new(0, 1))
  447.             #when "a" then move(-1, 0)
  448.             when "a" then move(Node.new(-1, 0))
  449.             #when "d" then move( 1, 0)
  450.             when "d" then move(Node.new(1, 0))
  451.         end
  452.         #Check if winning scenario was reached yet
  453.         winningScenario()
  454.     end
  455.    
  456.     #Moves the character in x and y direction
  457.     def move(node)
  458.         if isValidMove(node)
  459.             clearCurrentPosition()
  460.             updatePosition(node)
  461.         end
  462.     end
  463.    
  464.     def updateField(node, val)
  465.         @playfield[node.y][node.x] = val
  466.     end
  467.    
  468.     #Clears the current position back to walkable area
  469.     def clearCurrentPosition()
  470.         updateField(@gNode, 0)
  471.     end
  472.    
  473.     #This method should never be called without calling isValidMove(x,y) first!
  474.     #It updates the character's position in the playfield
  475.     def updatePosition(node)
  476.         @gNode.x += node.x
  477.         @gNode.y += node.y
  478.         updateField(@gNode, 1)
  479.     end
  480.    
  481.     #Performs a check to see if the move in x,y direction is valid
  482.     def isValidMove(node)
  483.         #Check if the move in (x,y) direction is still inside the array
  484.         if (@gNode.x+node.x >= 0) && (@gNode.x+node.x <= @cols-1) && (@gNode.y+node.y >= 0) && (@gNode.y+node.y <= @rows-1)
  485.             #Check if the position is walkable.
  486.             if @playfield[@gNode.y+node.y][@gNode.x+node.x] != 5
  487.                 #Also check if a powerup is going to be picked up
  488.                 if(@playfield[@gNode.y+node.y][@gNode.x+node.x] == 9)
  489.                     @collected += 1
  490.                 end
  491.                
  492.                 return true
  493.             end
  494.         end
  495.        
  496.         return false
  497.     end
  498. end
  499.  
  500. # = Main Program =
  501. #Reads user Input and clears buffer every time (not optimal, but good enough for now)
  502. def readKey()
  503.     char = Win32API.new('crtdll','_getch', [], 'L').Call
  504.     clearBuffer()
  505.     return char
  506. end
  507.  
  508. def clearBuffer()
  509.     system('cls')
  510. end
  511.  
  512. #Inits the program
  513. def init()
  514.     #Initialize game
  515.     clearBuffer()
  516.    
  517.     puts "##########################################################"
  518.     puts "#          Welcome to the crazy maze game!               #"
  519.     puts "##########################################################"
  520.     puts
  521.     puts "How big should the playing field be?"
  522.     print "Width: "
  523.     width = gets
  524.     print "Height: "
  525.     height = gets
  526.  
  527.     #Clamp the values so they're within (5 <= h <= 23) and (5 <= w <= 65)
  528.     height = [[5, height.to_i].max, 21].min
  529.     width = [[5, width.to_i].max, 63].min
  530.  
  531.     #Make sure the values are odd
  532.     if (width%2 == 0)
  533.         width += 1
  534.     end
  535.    
  536.     if (height%2 == 0)
  537.         height += 1
  538.     end
  539.    
  540.     #Inits the game - dimensions, playerfigure representation, amount of powerups
  541.     $game = Game.new(height,width, "@", 15, ".")
  542.     $game.init()
  543. end
  544.  
  545.  
  546. running = true
  547. init()
  548.  
  549. #Render loop
  550. while running
  551.     $game.render()
  552.     $game.update(readKey())
  553.    
  554.     running = $game.isRunning()
  555. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement