Class: Nim::Nim

Inherits:
Object
  • Object
show all
Defined in:
lib/nim/nim_core.rb

Instance Method Summary collapse

Constructor Details

#initializeNim

游戏规则:分成几摊的豆子,两个玩家轮流拣,一次可拿任意一摊里的至少一颗,拣掉最后一粒豆子的玩家判负。



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
# File 'lib/nim/nim_core.rb', line 6

def initialize
  puts %q{
You and Q take turns nim beans from distinct heaps, 
On each turn, you must nim at least one bean, and may nim any number of beans provided they all come from the same heap.

Whom nim the last one bean were *lost*

}

  a = [1,3,5,7]
  b = [3,4,5,6]
  puts "heap 1 (#{a.inject{|x, sum| x + sum}} beans)"
  draw0(a)
  puts "heap 2 (#{b.inject{|x, sum| x + sum}} beans)"
  draw0(b)
  
  loop do
    print 'choose the heap 1/2: '
    heap = gets.to_i
    if [1,2].include?(heap)
      @mat = heap == 1 ? a : b
      break 
    end
    puts %q{    inputs: 1 or 2
}
  end
  
  init_unsafe_positions
  
  hand = 1
  
  loop do
    print 'nim first or second 1/2: '
    g = gets
    if g.strip == "unsafe"
      @unsafe_positions.each{|x| puts x.join ' '}
      draw_mat
    else
      hand = g.to_i
      break if [1,2].include?(hand)
      puts %q{    inputs: 1 or 2
}
    end
  end
  puts 
  
  ai_pick if hand == 2
  
  loop do
  
    loop do
      print 'you nim: '
      args = gets.split ' '
      
      if args.size == 1
        iarg0 = args[0].to_i
        
        if iarg0 > 0 and iarg0 < 10 #4
          pos = iarg0 - 1
          if (0...@mat.size).include?(pos) and @mat[pos] > 0
            pick(pos)
            break
          end
        elsif iarg0 > 10 and iarg0 < 100  #45
          pos = iarg0 / 10 - 1
          num = iarg0 % 10
          if (0...@mat.size).include?(pos) and num > 0 and @mat[pos] >= num
            pick(pos, num)
            break
          end
        end
        
      elsif args.size == 2
        iarg0 = args[0].to_i
        iarg1 = args[1].to_i
        
        if iarg0 > 0 and iarg0 < 10 and iarg1 > 0 and iarg1 < 10  #4 5
          pos = iarg0 -1
          num = iarg1
          if (0...@mat.size).include?(pos) and @mat[pos] >= num
            pick(pos, num)
            break
          end
        end
      end
      puts %q{    invalid inputs.
nim least 1 beans from a line.
nim 5 beans from line 4, inputs: 45 or 4 5
nim all beans from line 4, inputs: 4
}
    end
    
    if @mat == [0,0,0,0]
      puts 'you *LOSE* :('
      break
    end
    
    ai_pick
    if @mat == [0,0,0,0]
      puts 'you *WIN* :)'
      break
    end
  end
  
end

Instance Method Details

#ai_pickObject



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
156
157
158
159
160
161
# File 'lib/nim/nim_core.rb', line 113

def ai_pick
  #行动集
  actions = Hash.new

  @unsafe_positions.each do |unsafe|
    dup = unsafe.dup

    scoped_index = 0
    scoped_value = 0

    @mat.each_with_index do |x, i|
      idx = dup.index(x)
      if idx
        dup.delete_at(idx)
      else
        #暂记不相等的value和它的下标
        scoped_index = i
        scoped_value = x
      end
    end
    
    if dup.size == 1 and scoped_value > dup.first
      actions[scoped_index] = scoped_value - dup.shift
    end
  end
  
  #拣取的下标和数量
  pos = num = 0
  
  if actions.size > 0
    #随机挑一个候选执行
    keys = actions.keys
    pos = keys[Random.new.rand(0...keys.size)]
    num = actions[pos]
  else
    #值不为0的下标集
    pos_ex = []
    @mat.each_with_index do |x, i|
      pos_ex << i if x > 0
    end
    
    #从不为0的堆中随机找一堆,随机拣取n颗
    pos = pos_ex[Random.new.rand(0...pos_ex.size)]
    num = Random.new.rand(1..@mat[pos])
  end

  puts "Q nim: #{(pos + 1) * 10 + num}"
  pick(pos, num)
end

#draw(mat) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/nim/nim_core.rb', line 176

def draw(mat)
  graph = ''
  space = ' '
  dot = '.'
  
  mat.each_with_index do |c, i|
    graph << "#{space * 4} #{i + 1} #{space * (3 - i)} #{(dot + space) * c} \n"
  end
  
  puts "#{graph}\n"
end

#draw0(mat) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
# File 'lib/nim/nim_core.rb', line 189

def draw0(mat)
  graph = ''
  space = ' '
  dot = '.'
  
  mat.each_with_index do |c, i|
    graph << "#{space * 4} #{space * (3 - i)} #{(dot + space) * c} \n"
  end
  
  puts "#{graph}\n"
end

#draw_matObject



171
172
173
# File 'lib/nim/nim_core.rb', line 171

def draw_mat
  draw(@mat)
end

#getsObject



248
249
250
# File 'lib/nim/nim_core.rb', line 248

def gets
  STDIN.gets
end

#init_unsafe_positionsObject



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/nim/nim_core.rb', line 202

def init_unsafe_positions
  @unsafe_positions = []
  #初始化阵集
  arr = [] 
  for a in 0..@mat[0]
    for b in a..@mat[1]
      for c in b..@mat[2]
        for d in c..@mat[3]
          arr << [a, b, c, d]
        end
      end
    end
  end
  
  #remove [0,0,0,0]
  arr.shift 
  
  #first unsafe-position is [0,0,0,1]
  @unsafe_positions << arr.shift 
  
  #从小到大遍历阵集,增加unsafe
  arr.each do |sample|
    is_safe = false
    
    @unsafe_positions.each do |unsafe|
    
      dup = unsafe.dup
      sample.each_with_index do |x, i|
        j = dup.index(x)
        dup.delete_at(j) if j
      end
      
      #有一堆豆子数不相等(多出) => 可以拿成一种unsafe => 是安全的
      if dup.size == 1
        is_safe = true
        break
      end
    end
    
    @unsafe_positions << sample unless is_safe
  end
  
  puts
  draw_mat
end

#pick(pos, num = ) ⇒ Object



164
165
166
167
168
# File 'lib/nim/nim_core.rb', line 164

def pick(pos, num=@mat[pos])
  @mat[pos] -= num
  puts
  draw_mat
end