From d0c124a31257fef31d319ed33f166e8f4c32e87b Mon Sep 17 00:00:00 2001 From: Alex Scown Date: Sat, 8 Jan 2022 20:11:47 +0000 Subject: [PATCH] Initial port Game works, but AI behaves differently to the original. Need to try and remove the globals. --- 04_Awari/ruby/README.md | 2 +- 04_Awari/ruby/awari.rb | 286 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 04_Awari/ruby/awari.rb diff --git a/04_Awari/ruby/README.md b/04_Awari/ruby/README.md index fb32811e..9c8c96bf 100644 --- a/04_Awari/ruby/README.md +++ b/04_Awari/ruby/README.md @@ -1,3 +1,3 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) -Conversion to [Ruby](https://www.ruby-lang.org/en/) +Conversion to [Ruby](https://www.ruby-lang.org/en/) by [Alex Scown](https://github.com/TheScown) diff --git a/04_Awari/ruby/awari.rb b/04_Awari/ruby/awari.rb new file mode 100644 index 00000000..aaf29632 --- /dev/null +++ b/04_Awari/ruby/awari.rb @@ -0,0 +1,286 @@ +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 + print n.to_s + 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}" + + input = gets.chomp.strip + scanner = StringScanner.new(input) + input_values = [] + + until scanner.eos? + scanner.scan(/\s+/) + + if scanner.check(/"/) + scanner.scan(/"/) + next_string = scanner.scan_until(/"/) + + if next_string + # Remove the trailing close quote + next_string.chomp!('"') + else + # No close quote – Vintage Basic crashes in this case + raise ArgumentError('Unmatched quotes in input') + end + elsif scanner.exist?(/,/) + next_string = scanner.scan_until(/,/).chomp(',') + else + next_string = scanner.scan_until(/\s+|$/).rstrip + end + + input_values << next_string + end + + input_values << '' if input_values.empty? + + 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_internals(beans) + $k = $k - 7 if $k > 6 + $c = $c + 1 + + $f[$n] = $f[$n] * 6 + $k if $c < 9 + + unless beans[0...6].find { |b| b != 0 } && beans[7...13].find { |b| b != 0 } + $game_over = true + end +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_internals(beans) + + last_pit +end + +def end_game(beans) + puts + puts "GAME OVER" + + difference = beans[6] - beans[13] + + if difference < 0 + puts "I WIN BY #{-difference} POINTS" + + return + end + + $n += 1 + + puts "YOU WIN BY #{difference} POINTS" if difference > 0 + puts "DRAWN GAME" if difference == 0 +end + +# @param [Array] beans +def get_computer_move(beans) + d = -99 + home_pit = 13 + beans_copy = beans.dup + + move_to_do = 0 + + (7..12).each do |move_under_test| + if beans[move_under_test] == 0 + next + end + + max_score = 0 + + distribute_beans(beans_copy, move_under_test, home_pit) + + (0...6).each do |i| + next if beans_copy[i] == 0 + + l = beans_copy[i] + i + r = l / 14 + l_mod_14 = l % 14 + + r = beans_copy[12 - l_mod_14] + r if beans_copy[l_mod_14] == 0 && l_mod_14 != 6 && l_mod_14 != 13 + + max_score = r if r > max_score + end + + final_score = beans_copy[13] - beans_copy[6] - max_score + + if $c <=8 + $k = move_under_test + $k = $k - 7 if $k > 6 + end + + (1...$n).each do |i| + final_score = final_score - 2 if $f[$n] * 6 + $k == (($f[i]/6 ** (7-$c)) + 0.1).floor + end + + if final_score >= d + move_to_do = move_under_test + d = final_score + end + end + + last_pit = perform_move(beans, move_to_do, home_pit) + + [move_to_do, last_pit] +end + +puts 'AWARI'.center(80) +puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(80) + +# Initialise stable variables +$f = Array.new(50) +$n = 0 + +# APPLICATION LOOP +:game +while true + print "\n", "\n" + beans = Array.new(13, 3) + beans[6] = 0 + beans[13] = 0 + + $f[$n] = 0 + $game_over = false + $c = 0 + + until $game_over + print_beans(beans) + + move = get_move("YOUR MOVE", beans) + home_pit = 6 + computer_home_pit = 13 + $k = move + + last_pit = perform_move(beans, move, home_pit) + + print_beans(beans) + + if $game_over + end_game(beans) + next :game + end + + if home_pit == last_pit + second_move = get_move("AGAIN", beans) + $k = second_move + + perform_move(beans, second_move, home_pit) + + print_beans(beans) + + if $game_over + end_game(beans) + next :game + end + end + + computer_move, computer_last_pit = get_computer_move(beans) + print "MY MOVE IS #{computer_move - 6}" + + if $game_over + end_game(beans) + next :game + end + + if computer_last_pit == computer_home_pit + second_computer_move, _ = get_computer_move(beans) + print ",#{second_computer_move - 6}" + + if $game_over + end_game(beans) + next :game + end + end + end +end