1
0
Fork 0

Use dependency injection to provide various things

This commit is contained in:
Andrew Tomaka 2022-01-26 23:59:05 -05:00
parent b4a061c6d7
commit 9755aed007
Signed by: atomaka
GPG Key ID: 61209BF70A5B18BE
7 changed files with 245 additions and 0 deletions

12
driver.rb Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env ruby
require_relative "lib/game"
game = Game.new
trap "SIGINT" do
game.exit!
exit 130
end
game.play

View File

@ -0,0 +1,91 @@
require "capybara"
require "selenium-webdriver"
require "webdrivers"
module Board
class WordleUnlimited
attr :guesses, :session
def initialize
@guesses = []
@session = Capybara::Session.new(:selenium_chrome)
end
def start
session.visit("https://www.wordleunlimited.com/")
end
def answer(guess)
guess.chars.map(&:upcase).each { |letter| click(letter) }
click("Enter")
answer_invalid? ? clear_answer! : @guesses << guess
end
def reset!
@guesses = []
click("Enter")
end
def winner?
session.has_text?(:visible, "Winner!", wait: 0)
end
def loser?
session.has_text?(:visible, "You lost!", wait: 0)
end
def allowed_letters
(exact_letters + elsewhere_letters).uniq
end
def bad_letters
session
.find_all('div.Game-keyboard-button.letter-absent', wait: 0)
.map(&:text)
.map(&:downcase)
end
def close!
@session.quit
end
def first_guess?
guesses.empty?
end
def correct_answer
session.find('div.feedback > div > b').text
end
private
def exact_letters
session
.find_all('div.Game-keyboard-button.letter-correct', wait: 0)
.map(&:text)
.map(&:downcase)
end
def elsewhere_letters
session
.find_all('div.Game-keyboard-button.letter-elsewhere', wait: 0)
.map(&:text)
.map(&:downcase)
end
def answer_invalid?
session.has_text?(:visible, "Not a valid word", wait: 2)
end
def click(key)
session
.find('div.Game-keyboard-button', text: /\A#{key}\Z/, wait: 0)
.click()
end
def clear_answer!
5.times { click("") }
end
end
end

View File

@ -0,0 +1,13 @@
module Dictionary
class Dictionary
attr_accessor :file
def initialize(file: "dictionary.txt")
@file = file
end
def words
@words ||= File.readlines(file).map(&:strip)
end
end
end

69
lib/game.rb Normal file
View File

@ -0,0 +1,69 @@
require "debug"
require_relative "outcome"
require_relative "board/wordle_unlimited"
require_relative "dictionary/dictionary"
require_relative "strategy/naive"
require_relative "strategy/wheel_of_fortune"
class Game
attr_reader :board, :dictionary, :start_strategy, :strategy, :outcomes
def initialize(
board: Board::WordleUnlimited,
dictionary: Dictionary::Dictionary,
start_strategy: Strategy::WheelOfFortune,
strategy: Strategy::Naive
)
@board = board.new
@dictionary = dictionary.new
@start_strategy = start_strategy.new(dictionary: @dictionary)
@strategy = strategy.new(dictionary: @dictionary)
@outcomes = []
end
def play
board.start
loop do
guess_strategy = board.first_guess? ? start_strategy : strategy
guess = guess_strategy
.guess(
good_letters: board.allowed_letters,
bad_letters: board.bad_letters,
guesses: board.guesses,
)
board.answer(guess)
if board.winner?
@outcomes << Outcome.new(
state: :win,
correct: board.correct_answer,
guesses: board.guesses,
)
board.reset!
elsif board.loser?
@outcomes << Outcome.new(
state: :loss,
correct: board.correct_answer,
guesses: board.guesses,
)
board.reset!
end
end
end
def exit!
board.close!
puts
puts "=" * 80
puts "Won: #{outcomes.select(&:win?).count}"
puts "Lost: #{outcomes.select(&:loss?).count}"
puts "=" * 80
end
end

17
lib/outcome.rb Normal file
View File

@ -0,0 +1,17 @@
class Outcome
attr :correct, :guesses, :state
def initialize(state:, correct:, guesses:)
@state = state
@correct = correct
@guesses = guesses
end
def win?
state == :win
end
def loss?
state == :loss
end
end

19
lib/strategy/naive.rb Normal file
View File

@ -0,0 +1,19 @@
module Strategy
class Naive
WORD_SIZE = 5
attr :dictionary
def initialize(dictionary:)
@dictionary = dictionary
end
def guess(good_letters:, bad_letters:, **args)
dictionary.words
.select { |word| word.length == WORD_SIZE }
.reject { |word| bad_letters.any? { |letter| word.chars.include?(letter) } }
.select { |word| (good_letters - word.chars).length == 0 }
.sample
end
end
end

View File

@ -0,0 +1,24 @@
module Strategy
class WheelOfFortune
WORD_SIZE = 5
attr :dictionary
def initialize(dictionary:)
@dictionary = dictionary
end
def guess(**args)
dictionary.words
.select { |word| word.length == WORD_SIZE }
.select { |word| (start_letters - word.chars).length == 1 }
.sample
end
private
def start_letters
%w(r s t l n e)
end
end
end