F28306cdcd0269dd76f89bc1ef4d9adc

You probably know the game of connect-four. 2 player alternatively drop a token in a grid (which i called board). I wrote the following ruby classes for the game. The board is organized in columns (in which you drop the tokens).
All suggestions are welcome, i'll enjoy seeing any suggestions or variations !
I'm an experienced developer, but not in Ruby.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
class ConnectFourBoard
  AIM = 4 # number of neighbouring tokens to obtain

  def initialize(rows,columns)
    @rows = rows  # number of rows
    @columns = columns  # number of columns
    @board = Array.new(@columns)
  end

  #  A player drop a token in the board at a certain position
  def drop_token(player,column)
    if @board[column] == nil
      @board[column] = Array.new
    end
    if @board[column].length == @rows
      raise RuntimeException
    end
    @board[column].push(Token.new(player))
  end

  # check the presencd of a token at a certain position in the board
  def position(row,column)
    col = @board[column]
    if !col.nil?
      position = col[row];
    else 
      return nil
    end
  end
  
  # empty board
  def reset
    @board.each do |column|
      column = nil
    end
    @board = nil
    initialize(@rows,@columns)
  end
  
  # check if a player has won
  def check_winner(player)
    win = vertical_count(player) || horizontal_count(player) ||
          diagonal_count(player) || back_diagonal_count(player)
  end

 private
  # check if there was a victory in vertical direction of board
  def vertical_count(player)
    win = false
    @board.each do |column|
      counter = 0
      if column.nil?
        next
      end
      column.each do |position|
        counter = increment(position,player,counter)
        win = win || win(counter) #if won it stays won
      end
    end
    win # return if won
  end

  # check if there was a victory in the horizontal direction of board
  def horizontal_count(player)
    win = false
    (0..@rows-1).each do |row|
      counter = 0
      @board.each_with_index do |column,column_index|
        if column.nil?
          counter = 0
        end
        position = position(row,column_index)
        counter = increment(position,player,counter)
        win = win || win(counter) # if won it stays won
      end
    end

    win # return if won
  end

# back_diagonal_count counts the tokens from a same player in diagonal line
# the "back" stands for \ (backslash) as in the direction of the diagonal
  def back_diagonal_count(player)
    win = false
    (AIM-1..@columns-1).each do |i|
      counter = 0
      rowlimit = @rows-1 > i ? i : @rows-1
      (0..rowlimit).each do |j|
        position = position (j,i-j)
        counter = increment(position,player,counter)
        win = win || win(counter)
      end
    end
    win # return if won
  end
  
# diagonal_count counts the tokens from a same player in diagonal line
# the counting happens in the / direction (slash) = direction of the diagonal 
  def diagonal_count(player)
    win = false
    (0..@columns-AIM).each do |i|
      counter = 0
      rowlimit = @rows-1-i
      (0..rowlimit).each do |j|
         position = position(j,i+j)
         counter = increment(position,player,counter)
         win = win || win(counter)
      end
    end    
    win
  end

  def increment(position,player,counter)
    if position && position.player == player
      return counter.next
    else
      return 0
    end
  end
  
  def win(counter)
    if counter >= AIM
      return true
    else
      return false
    end    
  end

# for debugging purposes: to display the board as 0 and 1  
  def board_debug
    (0..@rows-1).each do |row|  
      @board.each_with_index do |column,index|
        position = position(@rows-1-row,index)
        if position 
          printf("#{position.player.to_i}")
        else
          printf(" ")
        end
      end
      printf("\n")
    end  
  end
end

# class token in board
class Token
  def initialize(player)
    @player = player
  end
  def player
    @player
  end
end


Refactorings

No refactoring yet !

5170ca260dbd2cdfd5a887a4dba7636f

Jeremy Weiskotten

January 2, 2008, January 02, 2008 00:48, permalink

No rating. Login to rate!

Some minor changes... original algorithm is pretty much intact but you could simplify if a bit. The changes I made were mostly to use common Ruby conventions and idioms, like ||= and question marks in the names of methods that return a Boolean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
class ConnectFourBoard
  AIM = 4 # number of neighbouring tokens to obtain

  def initialize(rows, columns)
    @rows = rows  # number of rows
    @columns = columns  # number of columns
  end

  # A player drop a token in the board at a certain position
  def drop_token(player, column)
    col = get_column(column)
    raise RuntimeException if col.length == @rows
    col << Token.new(player)
  end

  # check the presence of a token at a certain position in the board
  def position(row, column)
    col = get_column(column)
    return col[row] unless col.nil?
  end
  
  # empty board
  def reset
    @board = nil
  end
  
  # check if a player has won
  def check_winner?(player)
    vertical_count?(player) || 
    horizontal_count?(player) ||
    diagonal_count?(player) || 
    back_diagonal_count?(player)
  end

  private
 
  def board
    # lazy initialize the game board
    @board ||= Array.new(@columns)
  end
  
  def get_column(c)
    # lazy initialize the column
    board[c] ||= []
  end
  
  def max_row_index
    @rows - 1
  end
 
  # check if there was a victory in vertical direction of board
  def vertical_count?(player)
    win = false
    board.each do |column|
      unless column.nil?
        counter = 0
        column.each do |position|
          counter = increment(position, player, counter)
          win ||= win?(counter) #if won it stays won
        end
      end
    end
    win # return if won
  end

  # check if there was a victory in the horizontal direction of board
  def horizontal_count?(player)
    win = false
    (0...@rows).each do |row|
      counter = 0
      board.each_with_index do |column, column_index|
        counter = 0 if column.nil?
        position = position(row, column_index)
        counter = increment(position, player, counter)
        win ||= win?(counter) # if won it stays won
      end
    end

    win # return if won
  end

  # back_diagonal_count counts the tokens from a same player in diagonal line
  # the "back" stands for \ (backslash) as in the direction of the diagonal
  def back_diagonal_count?(player)
    win = false
    (AIM-1...@columns).each do |i|
      counter = 0
      rowlimit = max_row_index > i ? i : max_row_index
      (0..rowlimit).each do |j|
        position = position (j,i-j)
        counter = increment(position, player, counter)
        win ||= win?(counter)
      end
    end
    win # return if won
  end
  
  # diagonal_count counts the tokens from a same player in diagonal line
  # the counting happens in the / direction (slash) = direction of the diagonal 
  def diagonal_count?(player)
    win = false
    (0..@columns-AIM).each do |i|
      counter = 0
      rowlimit = max_row_index-i
      (0..rowlimit).each do |j|
         position = position(j,i+j)
         counter = increment(position, player, counter)
         win ||= win?(counter)
      end
    end    
    win
  end

  def increment(position, player, counter)
    if position && position.player == player
      return counter.next
    else
      return 0
    end
  end
  
  def win?(counter)
    counter >= AIM
  end

# for debugging purposes: to display the board as 0 and 1  
  def board_debug
    (0...@rows).each do |row|  
      board.each_with_index do |column, index|
        position = position(max_row_index-row, index)
        if position 
          printf("#{position.player.to_i}")
        else
          printf(" ")
        end
      end
      printf("\n")
    end  
  end
end

# class token in board
class Token
  def initialize(player); @player = player; end
  def player; @player; end
end

Your refactoring





Format Copy from initial code

or Cancel