Move remaining functions into class

This commit is contained in:
Alex Scown
2022-01-09 13:55:27 +00:00
parent 30cedd2154
commit 126e554e95

View File

@@ -1,32 +1,5 @@
require 'strscan'
def print_beans(beans)
puts
# Print computer beans
print " "
beans[7..12].reverse.each { |bean_count| print_bean(bean_count) }
puts
# Print home beans
print_bean(beans[13])
print ' '
print_number(beans[6]) # This is not print_bean in line with the original version
puts
# Print player beans
print " "
beans[0..5].each { |bean_count| print_bean(bean_count) }
puts
puts
end
def print_bean(bean_count)
print ' ' if bean_count < 10
print_number(bean_count)
end
def print_number(n)
# PRINT adds padding after a number and before a positive number
print ' ' if n >= 0
@@ -34,38 +7,6 @@ def print_number(n)
print ' '
end
def get_move(prompt, beans)
move = get_integer_input(prompt)
while move < 1 || move > 6 || beans[move - 1] == 0
puts "ILLEGAL MOVE"
move = get_integer_input("AGAIN")
end
move - 1
end
def get_integer_input(prompt)
integer_value = nil
input_values = input(prompt)
while integer_value.nil?
print '!EXTRA INPUT IGNORED' if (input_values.size > 1)
value = input_values.first
begin
integer_value = Integer(value)
rescue
puts '!NUMBER EXPECTED - RETRY INPUT LINE'
input_values = input('')
end
end
integer_value
end
def input(prompt)
prompt_suffix = '? '
print "#{prompt}#{prompt_suffix}"
@@ -102,131 +43,6 @@ def input(prompt)
input_values
end
def distribute_beans(beans, start_pit, home_pit)
beans_to_distribute = beans[start_pit]
beans[start_pit] = 0
current_pit = start_pit
(0...beans_to_distribute).each do
current_pit = (current_pit + 1) % beans.size
beans[current_pit] += 1
end
# If the last pit was empty before we put a bean in it (and it's not a scoring pit), add beans to score
if beans[current_pit] == 1 && current_pit != 6 && current_pit != 13 && beans[12 - current_pit] != 0
beans[home_pit] = beans[home_pit] + beans[12 - current_pit] + 1
beans[current_pit] = 0
beans[12 - current_pit] = 0
end
current_pit
end
def update_history(current_move)
k = current_move % 7
$c = $c + 1
$history[$non_win_count] = $history[$non_win_count] * 6 + k if $c < 9
end
# @param [Array] beans
# @param [Integer] move
# @param [Integer] home_pit
def perform_move(beans, move, home_pit)
last_pit = distribute_beans(beans, move, home_pit)
update_history(move)
last_pit
end
# @return [Boolean] True if the computer did not win
def end_game(beans)
puts
puts "GAME OVER"
difference = beans[6] - beans[13]
if difference < 0
puts "I WIN BY #{-difference} POINTS"
return
end
$non_win_count += 1
puts "YOU WIN BY #{difference} POINTS" if difference > 0
puts "DRAWN GAME" if difference == 0
difference >= 0
end
# @param [Array] beans
def get_computer_move(beans)
d = -99
home_pit = 13
chosen_move = 7
# Test all possible moves
(7...13).each do |move_under_test|
# Create a copy of the beans to test against
beans_copy = beans.dup
# If the move is not legal, skip it
next if beans[move_under_test] == 0
# Determine the best response the player may make to this move
player_max_score = 0
# Make the move under test against the copy
distribute_beans(beans_copy, move_under_test, home_pit)
# Test every player response
(0...6).each do |i|
# Skip the move if it would be illegal
next if beans_copy[i] == 0
# Determine the last
landing_with_overflow = beans_copy[i] + i
# If landing > 13 the player has put a bean in both home pits
player_move_score = (landing_with_overflow > 14) ? 1 : 0
# Find the actual pit
landing = landing_with_overflow % 14
# If the landing pit is empty, the player will steal beans
if beans_copy[landing] == 0 && landing != 6 && landing != 13
player_move_score = beans_copy[12 - landing] + player_move_score
end
# Update the max score if this move is the best player move
player_max_score = player_move_score if player_move_score > player_max_score
end
# Final score for move is computer score, minus the player's score and any player gains from their best move
final_score = beans_copy[13] - beans_copy[6] - player_max_score
if $c < 9
k = move_under_test % 7
(0...$non_win_count).each do |i|
final_score = final_score - 2 if $history[$non_win_count] * 6 + k == ((Float($history[i]) / 6 ** (7 - $c)) + 0.1).floor
end
end
# Choose the move if it is the best move found so far
if final_score >= d
chosen_move = move_under_test
d = final_score
end
end
last_pit = perform_move(beans, chosen_move, home_pit)
[chosen_move, last_pit]
end
class Game
def initialize(history, non_win_count)
@beans = Array.new(13, 3)
@@ -239,74 +55,251 @@ class Game
@non_win_count = non_win_count
end
def play
while true
print_beans
move = get_move("YOUR MOVE")
home_pit = 6
computer_home_pit = 13
last_pit = perform_move(move, home_pit)
print_beans
break if game_over
if home_pit == last_pit
second_move = get_move("AGAIN")
perform_move(second_move, home_pit)
print_beans
break if game_over
end
computer_move, computer_last_pit = get_computer_move
print "MY MOVE IS #{computer_move - 6}"
break if game_over
if computer_last_pit == computer_home_pit
second_computer_move, _ = get_computer_move
print ",#{second_computer_move - 6}"
break if game_over
end
end
end_game
end
private
def game_over
@beans[0...6].all? { |b| b == 0 } || @beans[7...13].all? { |b| b == 0 }
end
def play
until game_over
print_beans(@beans)
# @return [Boolean] True if the computer did not win
def end_game
puts
puts "GAME OVER"
move = get_move("YOUR MOVE", @beans)
home_pit = 6
computer_home_pit = 13
difference = @beans[6] - @beans[13]
last_pit = perform_move(@beans, move, home_pit)
if difference < 0
puts "I WIN BY #{-difference} POINTS"
print_beans(@beans)
if game_over
return end_game(@beans)
end
if home_pit == last_pit
second_move = get_move("AGAIN", @beans)
perform_move(@beans, second_move, home_pit)
print_beans(@beans)
if game_over
return end_game(@beans)
end
end
computer_move, computer_last_pit = get_computer_move(@beans)
print "MY MOVE IS #{computer_move - 6}"
if game_over
return end_game(@beans)
end
if computer_last_pit == computer_home_pit
second_computer_move, _ = get_computer_move(@beans)
print ",#{second_computer_move - 6}"
if game_over
return end_game(@beans)
end
end
return
end
puts "YOU WIN BY #{difference} POINTS" if difference > 0
puts "DRAWN GAME" if difference == 0
difference >= 0
end
# @param [Integer] move
# @param [Integer] home_pit
def perform_move(move, home_pit)
last_pit = distribute_beans(move, home_pit)
update_history(move)
last_pit
end
def update_history(current_move)
k = current_move % 7
@turn_counter += 1
# Add the move to the history
@history[@non_win_count] = @history[@non_win_count] * 6 + k if @turn_counter < 9
end
def print_beans
puts
# Print computer beans
print ' ' * 3
@beans[7...13].reverse.each { |bean_count| print_bean(bean_count) }
puts
# Print home beans
print_bean(@beans[13])
print ' ' * 23
print_number(@beans[6]) # This is not print_bean in line with the original version
puts
# Print player beans
print ' ' * 3
@beans[0...6].each { |bean_count| print_bean(bean_count) }
puts
puts
end
def get_move(prompt)
move = get_integer_input(prompt)
while move < 1 || move > 6 || @beans[move - 1] == 0
puts "ILLEGAL MOVE"
move = get_integer_input("AGAIN")
end
move - 1
end
def distribute_beans(start_pit, home_pit, beans = @beans)
beans_to_distribute = beans[start_pit]
beans[start_pit] = 0
current_pit = start_pit
(0...beans_to_distribute).each do
current_pit = (current_pit + 1) % beans.size
beans[current_pit] += 1
end
# If the last pit was empty before we put a bean in it (and it's not a scoring pit), add beans to score
if beans[current_pit] == 1 && current_pit != 6 && current_pit != 13 && beans[12 - current_pit] != 0
beans[home_pit] = beans[home_pit] + beans[12 - current_pit] + 1
beans[current_pit] = 0
beans[12 - current_pit] = 0
end
current_pit
end
def print_bean(bean_count)
print ' ' if bean_count < 10
print_number(bean_count)
end
def get_integer_input(prompt)
integer_value = nil
input_values = input(prompt)
while integer_value.nil?
print '!EXTRA INPUT IGNORED' if (input_values.size > 1)
value = input_values.first
begin
integer_value = Integer(value)
rescue
puts '!NUMBER EXPECTED - RETRY INPUT LINE'
input_values = input('')
end
end
integer_value
end
# @param [Array] beans
def get_computer_move
d = -99
home_pit = 13
chosen_move = 7
# Test all possible moves
(7...13).each do |move_under_test|
# Create a copy of the beans to test against
beans_copy = @beans.dup
# If the move is not legal, skip it
next if beans_copy[move_under_test] == 0
# Determine the best response the player may make to this move
player_max_score = 0
# Make the move under test against the copy
distribute_beans(move_under_test, home_pit, beans_copy)
# Test every player response
(0...6).each do |i|
# Skip the move if it would be illegal
next if beans_copy[i] == 0
# Determine the last
landing_with_overflow = beans_copy[i] + i
# If landing > 13 the player has put a bean in both home pits
player_move_score = (landing_with_overflow > 14) ? 1 : 0
# Find the actual pit
landing = landing_with_overflow % 14
# If the landing pit is empty, the player will steal beans
if beans_copy[landing] == 0 && landing != 6 && landing != 13
player_move_score = beans_copy[12 - landing] + player_move_score
end
# Update the max score if this move is the best player move
player_max_score = player_move_score if player_move_score > player_max_score
end
# Final score for move is computer score, minus the player's score and any player gains from their best move
final_score = beans_copy[13] - beans_copy[6] - player_max_score
if @turn_counter < 9
k = move_under_test % 7
(0...@non_win_count).each do |i|
# Penalise move if it was used in a losing game
final_score = final_score - 2 if @history[@non_win_count] * 6 + k == ((Float(@history[i]) / 6 ** (7 - @turn_counter)) + 0.1).floor
end
end
# Choose the move if it is the best move found so far
if final_score >= d
chosen_move = move_under_test
d = final_score
end
end
last_pit = perform_move(chosen_move, home_pit)
[chosen_move, last_pit]
end
end
puts 'AWARI'.center(80)
puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(80)
# Initialise stable variables
$history = Array.new(50)
$non_win_count = 0
history = Array.new(50)
non_win_count = 0
# APPLICATION LOOP
while true
print "\n", "\n"
$history[$non_win_count] = 0
$c = 0
history[non_win_count] = 0
game = Game.new($history, $non_win_count)
game = Game.new(history, non_win_count)
computer_didnt_win = game.play
$non_win_count += 1 if computer_didnt_win
non_win_count += 1 if computer_didnt_win
end