From 6b591eaa350a2acd75340b0bb4ac0fe151239079 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Fri, 28 Jan 2022 21:52:46 -0500 Subject: [PATCH] Implement basic board intelligent strategy --- lib/board/wordle_unlimited.rb | 25 +++++++++++++++++++++++++ lib/game.rb | 17 ++++++++++++++--- lib/outcome.rb | 8 ++++++++ lib/played_board.rb | 21 +++++++++++++++++++++ lib/played_letter.rb | 20 ++++++++++++++++++++ lib/strategy/basic_board.rb | 31 +++++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 lib/played_board.rb create mode 100644 lib/played_letter.rb create mode 100644 lib/strategy/basic_board.rb diff --git a/lib/board/wordle_unlimited.rb b/lib/board/wordle_unlimited.rb index f80945d..cf2e1d4 100644 --- a/lib/board/wordle_unlimited.rb +++ b/lib/board/wordle_unlimited.rb @@ -3,6 +3,8 @@ require "selenium-webdriver" require "webdrivers" require_relative "../nonexistant_guess_error" +require_relative "../played_board" +require_relative "../played_letter" module Board class WordleUnlimited @@ -80,6 +82,29 @@ module Board end end + def state + PlayedBoard.new( + locked_in.map do |row| + letters = row + .find_all('div.RowL-letter', wait: 0) + .map do |letter| + state = if letter.[]("class").split.include?("letter-correct") + :correct + elsif letter.[]("class").split.include?("letter-elsewhere") + :elsewhere + elsif letter.[]("class").split.include?("letter-absent") + :absent + end + + PlayedLetter.new( + letter: letter.text.downcase, + state: state + ) + end + end + ) + end + private def exact_letters diff --git a/lib/game.rb b/lib/game.rb index f1482d0..3ab2b56 100644 --- a/lib/game.rb +++ b/lib/game.rb @@ -1,13 +1,17 @@ require "debug" +require "logger" -require_relative "outcome" require_relative "nonexistant_guess_error" +require_relative "outcome" +require_relative "played_board" +require_relative "played_letter" require_relative "board/wordle_unlimited" require_relative "dictionary/dictionary" require_relative "dictionary/live_dictionary" +require_relative "strategy/basic_board" require_relative "strategy/most_common" require_relative "strategy/naive" require_relative "strategy/template" @@ -16,11 +20,13 @@ require_relative "strategy/wheel_of_fortune" class Game attr_reader :board, :dictionary, :start_strategy, :strategy, :outcomes + attr :logger + def initialize( board: Board::WordleUnlimited, dictionary: Dictionary::LiveDictionary, - start_strategy: Strategy::MostCommon, - strategy: Strategy::Template + start_strategy: Strategy::Vowels, + strategy: Strategy::BasicBoard ) @board = board.new @dictionary = dictionary.new @@ -28,6 +34,8 @@ class Game @strategy = strategy.new(dictionary: @dictionary) @outcomes = [] + + @logger = Logger.new("loss-logger.log") end def play @@ -42,6 +50,7 @@ class Game bad_letters: board.bad_letters, guesses: board.guesses, template: board.template, + board: board.state, ) begin @@ -64,6 +73,8 @@ class Game guesses: board.guesses, ) board.reset! + + logger.info(outcomes.last.to_s) end end end diff --git a/lib/outcome.rb b/lib/outcome.rb index bb3d029..e7b9f2c 100644 --- a/lib/outcome.rb +++ b/lib/outcome.rb @@ -14,4 +14,12 @@ class Outcome def loss? state == :loss end + + def to_s + "#{state}: #{correct} guessing #{guesses_to_s}" + end + + def guesses_to_s + guesses.join(", ") + end end diff --git a/lib/played_board.rb b/lib/played_board.rb new file mode 100644 index 0000000..e648fd9 --- /dev/null +++ b/lib/played_board.rb @@ -0,0 +1,21 @@ +class PlayedBoard < Array + def good_letters + (correct_letters + elsewhere_letters).uniq + end + + def bad_letters + absent_letters - good_letters + end + + def correct_letters + flatten.select(&:correct?).uniq.map(&:letter) + end + + def elsewhere_letters + flatten.select(&:elsewhere?).uniq.map(&:letter) + end + + def absent_letters + flatten.select(&:absent?).uniq.map(&:letter) + end +end diff --git a/lib/played_letter.rb b/lib/played_letter.rb new file mode 100644 index 0000000..bed475a --- /dev/null +++ b/lib/played_letter.rb @@ -0,0 +1,20 @@ +class PlayedLetter + attr :letter, :state + + def initialize(letter:, state:) + @letter = letter + @state = state + end + + def correct? + state == :correct + end + + def elsewhere? + state == :elsewhere + end + + def absent? + state == :absent + end +end diff --git a/lib/strategy/basic_board.rb b/lib/strategy/basic_board.rb new file mode 100644 index 0000000..6104ef4 --- /dev/null +++ b/lib/strategy/basic_board.rb @@ -0,0 +1,31 @@ +module Strategy + class BasicBoard + attr :dictionary, :word_size + + def initialize(dictionary:, word_size: 5) + @dictionary = dictionary + @word_size = word_size + end + + def guess(board:, **args) + transposed_board = board.transpose + dictionary.words + .select { |word| word.length == word_size } + .reject { |word| (word.chars & board.bad_letters).any? } + .select { |word| (board.good_letters - word.chars).length == 0 } + .reject do |word| # any word has character where board has letter absent + transposed_board.each_with_index.any? do |letters, index| + letters.reject(&:correct?).map(&:letter).include?(word[index]) + end + end + .select do |word| # make sure correct letters used in right spot + transposed_board.each_with_index.all? do |letters, index| + correct_letter = letters.select(&:correct?).first + + !correct_letter || word[index] == correct_letter.letter + end + end + .sample + end + end +end