• 180行ruby代码搞定游戏2048


    最今在玩2048这款小游戏,游戏逻辑简单,很适合我这样的对于游戏新入行的人来实现逻辑。于是选择了最拿手的ruby语言来实现这款小游戏的主要逻辑。还是挺简单的,加起来4小时左右搞定。

    上代码:

    require 'optparse'
    
    
    module Help
    	HELP_TEXT =<<HELP
    
    press buttons for move
      l => move to left
      r => move to right
      t => move to top
      b => move to bottom
    press e button to exit game
    
    you can see this help text if your input ruby ruby_2048.rb --help
    HELP
    	def set_helps
    		OptionParser.new do |opts|
    			opts.on_tail("-h", "--help", 'This help text.') do
    		  	puts HELP_TEXT
    			exit!
    		end
    		end.parse!
    	end
    
    end
    
    
    class Object
    	def invoke(need, method)
    		if need
    			self.send(method) 
    		else
    			self
    		end
    	end
    end
    
    class R2048
    	extend Help
    
    	attr_reader :chessboard
    	LEFT = "l"
    	RIGHT = "r"
    	TOP = "t"
    	BOTTOM = "b"
    	EXIT = "e"
    
    	def initialize
    		R2048.set_helps
    	    @chessboard = Array.new(4){|x| Array.new(4){|y| 0}}
    	    @init_moved = false
    		1.upto(2){|i| generate_init_num}
    	end
    
    	def generate_init_num
    		return false unless @chessboard.flatten.uniq.select{|chess| chess == 0}.count > 0
    
    		rand_position = rand(16)
    		x, y = rand_position/4, rand_position % 4
    		until @chessboard[x][y] == 0
    			rand_position = rand(16)
    			x, y = rand_position/4, rand_position % 4
    	    end
    	    @chessboard[x][y] = [2, 4][rand(2)]
    
    	end
    
    	def check_and_merge(transpose, reverse)
    		moved = false
    		temp_chessboard = @chessboard.invoke(transpose, :transpose).map do |row|
    			reversed_row = set_jump_step(row.invoke(reverse, :reverse)).invoke(reverse, :reverse)
    			moved = true if reversed_row != row.invoke(reverse, :reverse)
    			reversed_row
    		end.invoke(transpose, :transpose)
    
    		if moved
    			@chessboard = temp_chessboard 
    			true
    		else
    			if !@init_moved
    				@init_moved = true
    				true
    			else
    			 	false
    			end
    		end
    	end
    
    	def generate_new_num(transpose, pos)
    		ungenerated = true
    
    		right_positions = []
    		@chessboard.invoke(transpose, :transpose).each_with_index{|row, i| right_positions << i if row[pos] == 0}
    		right_position = right_positions[rand(right_positions.count)]
    
    		row_index = 0
    		@chessboard = @chessboard.invoke(transpose, :transpose).map do |row|
    			if ungenerated && row_index == right_position
    				ungenerated = false
    				row[pos] = [2, 4][rand(2)]
    			end
    			row_index += 1
    			row
    		end.invoke(transpose, :transpose)
    		!ungenerated
    	end
    
    	def set_jump_step(row)
    	  pured = row.select{|chess| chess != 0 }.inject([]) do |sum, chess|
    	  	if sum.last == chess
    	  		sum.pop 
    	  		sum << chess * 2
    	  	else
    	  		sum << chess
    	  	end
    	  end
    	  pured.concat Array.new(4 - pured.count, 0)
    	end
    
    	def display
    	  puts "==============================="
          @chessboard.each_with_index do |c, row|
          	puts "#{c[0]}	#{c[1]}	#{c[2]}	#{c[3]}"
          	puts
          end
    	end
    
    	def failure_display
    		puts "you have failed!!!"
    	end
    
    	def run
    		display
    		key = nil
    		until key == "e
    "
    			key = gets
    			key.gsub!("
    ", "")
    			return if key == EXIT
    
    			if ![LEFT, RIGHT, TOP, BOTTOM].include? key
    				puts "input error" 
    				next
    			end
    
    			generate = case key
    			when LEFT
    				if check_and_merge(false, false)
    					generate_new_num(false, 3)
    				else
    					nil
    				end
    			when RIGHT
    				if check_and_merge(false, true)
    					generate_new_num(false, 0)
    				else
    					nil
    				end
    			when TOP
    				if check_and_merge(true, false)
    					generate_new_num(true, 3)
    				else
    					nil
    				end
    			when BOTTOM
    				if check_and_merge(true, true)
    					generate_new_num(true, 0)
    				else
    					nil
    				end
    			end
    
    			if generate == nil || generate
    			  display
    			else
    			  failure_display
    			  return
    			end
    		end
    	end
    end
    
    
    R2048.new.run
    
    

    写了一些測试:

    require 'ruby_2048'
    
    describe R2048 do
       before(:each) do
       	@r2048 = R2048.new
       end
       it "should jump to [2, 0, 0, 0] when input [0, 0, 0, 2]" do  
         @r2048.set_jump_step([0, 0, 0, 2]).should == [2, 0, 0, 0]
       end
    
       it "should jump to [2, 4, 0, 0] when input [2, 0, 4, 0]" do
       	@r2048.set_jump_step([2, 0, 4, 0]).should == [2, 4, 0, 0]
       end
    
       it "should jump to [4, 0, 0, 0] when input [2, 0, 2, 0]" do
       	@r2048.set_jump_step([2, 0, 2, 0]).should == [4, 0, 0, 0]
       end
    
       it "should jump to [2, 4, 4, 0] when input [2, 4, 2, 2]" do
       	@r2048.set_jump_step([2, 4, 2, 2]).should == [2, 4, 4, 0]
       end
    
       it "should jump to [4, 4, 0, 0] when input [2, 2, 2, 2]" do
       	@r2048.set_jump_step([2, 2, 2, 2]).should == [4, 4, 0, 0]
       end
    
    
    
       it "should + 1 chess if generate_init_num" do
       	expect { @r2048.generate_init_num }.to change{@r2048.chessboard.flatten.count{|chess| chess!= 0} }.by(1)  
       end
    
       it "should have already have two chess when inited" do
       	expect{@r2048.count} == 2
       end
    end

    貌似还不错,最新代码请见github:https://github.com/xumc/ruby_2048


    后记:

      一篇博客介绍c++版命令行2048居然写了500多行,见http://blog.csdn.net/yc7369/article/details/29416819, 恰恰证明了ruby的简洁。

  • 相关阅读:
    JDBC-HikariCP
    11、JDBC-Druid
    JDBC-DBCP
    JDBC-C3P0
    第十七篇-使用RadioGroup实现单项选择
    第十六篇-使用CheckBox实现多项选择
    第一篇-ubuntu18.04访问共享文件夹
    第十五篇-EditText做简单的登录框
    第十四篇-ImageButton控制聚焦,单击,常态三种状态的显示背景
    第十三篇-通过Button设置文本背景颜色
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5137319.html
Copyright © 2020-2023  润新知