Sunday, March 08, 2009

Rubyでオセロゲーム:Othero.rb

Ruby+Shoesでオセロゲームを作ってみた
ただまだ改良点がある
  • コンピューター対戦
  • ネットワーク対戦
othero.rb

   1  module Othero

   2    class Board

   3      Size = {:small => 6, :medium => 8, :large => 10}

   4      attr_reader :size

   5  

   6      def initialize(size= :small)

   7        @size = Size[size]

   8        @cells = Array.new(@size){ Array.new(@size) }

   9      end

  10  

  11      def [](row, col)

  12        @cells[col][row]

  13      end

  14  

  15      def []=(row, col, *colors)

  16        if colors[0]

  17          @cells[col][row] = Piece.new(row, col, *colors)

  18        else

  19          @cells[col][row] = nil

  20        end

  21      end

  22  

  23      def cells

  24        @cells

  25      end

  26    end

  27  

  28    class Piece

  29      attr_reader :col, :row, :face, :back

  30      def initialize(row, col, colors)

  31        @row, @col = row, col

  32        @face, @back = *colors

  33      end

  34  

  35      def to_s

  36        @face

  37      end

  38  

  39      def flip

  40        @face, @back = @back, @face

  41      end

  42    end

  43  end

  44  

  45  module Draw

  46    def initial_set

  47      c = Board.size/2

  48      Board[c, c] = Colors

  49      Board[c-1, c-1] = Colors

  50      Board[c-1, c] = Colors.reverse

  51      Board[c, c-1] = Colors.reverse

  52      draw_board("Start Game!")

  53    end

  54  

  55    def draw_board(msg=nil)

  56      clear do

  57        background black

  58        stack :margin => Margin do

  59          fill rgb(0, 190, 0)

  60          rect :left => 0, :top => 0, :width => BoardWidth, :height => BoardHeight

  61        end

  62  

  63        Board.cells.each_with_index do |rows, i|

  64          rows.each_with_index do |piece, j|

  65            left, top = i*CellWidth+Margin, j*CellHeight+Margin

  66            fill rgb(0, 440, 0, 90); strokewidth 1; stroke rgb(0, 100, 0)

  67            rect :left => left, :top => top, :width => CellWidth, :height => CellHeight

  68            if piece

  69              strokewidth 0

  70              fill (piece.to_s == Colors[1] ? rgb(155,155,155) : rgb(100,100,100))

  71              oval left+5, top+6, CellWidth-10, CellHeight-10

  72  

  73              fill (piece.to_s == Colors[1] ? Color_set[1] : Color_set[0])

  74              oval left+4, top+4, CellWidth-10, CellHeight-10

  75            else

  76              fill rgb(0, 190, 0)

  77              rect :left => left, :top => top, :width => CellWidth, :height => CellHeight

  78            end

  79          end

  80        end

  81        draw_message_board(msg)

  82      end

  83    end

  84  

  85    def draw_message_board(msg)

  86      stack :top => 610, :left => 0, :margin => Margin do

  87        background yellow

  88        para msg

  89      end

  90    end

  91  

  92    def find_cell(y, x)

  93      while x.between?(Margin, Margin+BoardWidth) and y.between?(Margin, Margin+BoardHeight)

  94        return (x-Margin)/CellWidth, (y-Margin)/CellHeight

  95      end

  96    end

  97  

  98    def current_status

  99      cnt_c1, cnt_c2 = 0, 0

 100      Board.cells.each_with_index do |rows, i|

 101        rows.each_with_index do |piece, j|

 102          next unless piece

 103          case piece.face

 104          when Colors[0]

 105            cnt_c1 += 1

 106          when Colors[1]

 107            cnt_c2 += 1

 108          end

 109        end

 110      end

 111      elapsed = (cnt_c1+cnt_c2)*100/Board.size**2

 112      [cnt_c1, cnt_c2, elapsed]

 113    end

 114  

 115    def winner?

 116      cnt_c1, cnt_c2 = current_status

 117      if (cnt_c1+cnt_c2) == Board.size**2 or (!flippable?(Colors) and !flippable?(Colors.reverse))

 118        if cnt_c1 > cnt_c2

 119          0

 120        elsif cnt_c1 < cnt_c2

 121          1

 122        else

 123          -1

 124        end

 125      else

 126        nil

 127      end

 128    end

 129  

 130    def flip_around?(piece, flip=false)

 131      flag = []

 132      [:E, :W, :S, :N, :NE, :NW, :SE, :SW].each do |dir|

 133        pieces = flippable_line(piece, dir)

 134        flag << pieces

 135        pieces.each { |p| p.flip } if flip

 136      end

 137      !flag.all?{ |p| p.empty? }

 138    end

 139  

 140    def flippable?(color)

 141      free_cells.each do |row, col|

 142        if flip_around?(Othero::Piece.new(row, col, color))

 143          return true

 144        end

 145      end

 146      false

 147    end

 148  

 149    private

 150    def flippable_line(piece, dir)

 151      row, col = piece.row, piece.col

 152      case dir

 153      when :E

 154        check_line(piece, row, col, op(:+, 0), op(:+, 1))

 155      when :W

 156        check_line(piece, row, col, op(:+, 0), op(:-, 1))

 157      when :S

 158        check_line(piece, row, col, op(:+, 1), op(:+, 0))

 159      when :N

 160        check_line(piece, row, col, op(:-, 1), op(:+, 0))

 161      when :NE

 162        check_line(piece, row, col, op(:-, 1), op(:+, 1))

 163      when :NW

 164        check_line(piece, row, col, op(:-, 1), op(:-, 1))

 165      when :SE

 166        check_line(piece, row, col, op(:+, 1), op(:+, 1))

 167      when :SW

 168        check_line(piece, row, col, op(:+, 1), op(:-, 1))

 169      end

 170    end

 171  

 172    def op(op, arg=1)

 173      lambda { |x| x.send(op, arg) }

 174    end

 175  

 176    def check_line(piece, row, col, op_row, op_col)

 177      row = op_row[row]; col = op_col[col]

 178      list = []

 179      while col.between?(0, Board.size-1) and row.between?(0, Board.size-1)

 180        if Board[row, col].nil?

 181          return list.clear

 182        elsif Board[row, col].face == piece.face

 183          return list

 184        else

 185          list << Board[row, col]

 186        end

 187        row = op_row[row]; col = op_col[col]

 188      end

 189      list.clear

 190    end

 191  

 192    def free_cells

 193      fc = []

 194      Board.cells.each_with_index do |rows, i|

 195        rows.each_with_index do |cell, j|

 196          fc << [j, i] unless cell

 197        end

 198      end

 199      fc

 200    end

 201  end

 202  

 203  Shoes.app :width => 620, :height => 670 do

 204    extend Draw

 205    Board = Othero::Board.new(:small)

 206  

 207    BoardWidth, BoardHeight = 600, 600

 208    Margin = 10

 209    CellWidth, CellHeight = BoardWidth/Board.size, BoardHeight/Board.size

 210    Colors = [:Black, :White]

 211    Color_set = [black, white]

 212  

 213    initial_set

 214  

 215    flag = true

 216  

 217    click do |button, left, top|

 218      row, col = find_cell(left, top)

 219      unless Board[row, col]

 220        Board[row, col] = flag ? Colors : Colors.reverse

 221        msg = if flip_around?(Board[row, col], true)

 222          flag = !flag

 223          "Yeh! Good one!"

 224        else

 225          Board[row, col] = nil

 226          "Wrong place!"

 227        end

 228  

 229        pt1, pt2, elapsed = current_status

 230  

 231        draw_board msg + " #{Colors[0]}:#{pt1} - #{Colors[1]}:#{pt2}(#{elapsed}%)" + " #{flag ? Colors[0] : Colors[1]} turn."

 232      end

 233    end

 234  

 235    release do |button, x, y|

 236      computer_turn

 237      if who = winner?

 238        pt1, pt2 = current_status

 239        if who

 240          alert "#{Colors[who]} wins!! by #{pt1 > pt2 ? pt1 : pt2}"

 241        else

 242          alert "Tie Game"

 243        end

 244        break

 245      end

 246      

 247      next_color = flag ? Colors : Colors.reverse

 248      unless flippable?(next_color)

 249        alert("#{next_color[0]} can't flip any pieces!")

 250        flag = !flag

 251        draw_message_board("#{next_color[1]} turn")

 252      end

 253    end

 254  end


No comments: