waylon-wordle 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,7 +24,7 @@ module Waylon
24
24
 
25
25
  def todays_wordle
26
26
  solution = solve_todays_wordle
27
- case solution.size
27
+ case solution.attempts.size
28
28
  when 1..2
29
29
  reply("I got *really* lucky today!")
30
30
  when 3
@@ -32,44 +32,40 @@ module Waylon
32
32
  when 4..5
33
33
  reply("I managed to figure it out.")
34
34
  when 6
35
- reply("I barely solved it!")
36
- else
37
- reply("Unfortunately, I didn't solve it.")
35
+ if solution.last_attempted_word == Waylon::Wordle.for_today
36
+ reply("I barely solved it!")
37
+ else
38
+ reply("Unfortunately, I didn't solve it.")
39
+ end
38
40
  end
39
41
 
40
- reply(formatted_wordle_score(solution))
42
+ reply(formatted_wordle_score(solution.attempts.map { _1[:result] }))
41
43
  end
42
44
 
43
45
  def spoil_wordle
44
46
  threaded_reply("Here's what my attempt looked like...")
45
47
  threaded_reply(
46
48
  codify(
47
- JSON.pretty_generate(solve_todays_wordle.map { |w| w.join.upcase })
49
+ JSON.pretty_generate(solve_todays_wordle.attempts.map { |w| w[:word].upcase })
48
50
  )
49
51
  )
50
52
  end
51
53
 
52
54
  private
53
55
 
54
- def formatted_wordle_score(solution)
55
- miss = "⬛"
56
- hit = "🟩"
57
- near_hit = "🟨"
58
- todays_answer = Waylon::Wordle.for_today.chars
59
- solved_in = solution.include?(todays_answer) ? solution.size : "X"
56
+ def formatted_wordle_score(scores)
57
+ translation = {
58
+ miss: "",
59
+ hit: "🟩",
60
+ near_hit: "🟨"
61
+ }
62
+
63
+ solved_in = scores.last.map(&:to_sym) == %i[hit hit hit hit hit] ? scores.size : "X"
60
64
 
61
65
  lines = ["Wordle #{Waylon::Wordle.todays_number} #{solved_in}/6"]
62
66
  lines << ""
63
- solution.each do |word|
64
- lines << word.map.with_index do |letter, index|
65
- if letter == todays_answer[index]
66
- hit
67
- elsif todays_answer.include?(letter)
68
- near_hit
69
- else
70
- miss
71
- end
72
- end.join
67
+ scores.each do |score|
68
+ lines << score.map { translation[_1.to_sym] }.join
73
69
  end
74
70
  lines.join("\n")
75
71
  end
@@ -78,6 +74,7 @@ module Waylon
78
74
  cache("solution_#{DateTime.now.new_offset("-05:00").to_date}", expires: 24 * 60 * 60) do
79
75
  solver = Waylon::Wordle::Solver.new(Waylon::Wordle.random_startword)
80
76
  solver.solve_todays_wordle
77
+ solver
81
78
  end
82
79
  end
83
80
  end
@@ -4,39 +4,83 @@ module Waylon
4
4
  module Wordle
5
5
  # Utility class for solving Wordle puzzles
6
6
  class Solver
7
- attr_reader :startword
7
+ attr_reader :startword, :answer
8
+ attr_accessor :attempts, :hits, :near_hits, :misses
8
9
 
9
10
  def initialize(startword)
10
11
  @startword = startword
12
+ @attempts = []
13
+ @hits = []
14
+ @near_hits = []
15
+ @misses = []
16
+ @answer = Waylon::Wordle.for_today.chars
11
17
  end
12
18
 
13
19
  def solve_todays_wordle
14
- answer = Waylon::Wordle.for_today.chars
15
- attempts = [startword.chars]
16
- attempts << make_attempt(answer, attempts) until attempts.last == answer || attempts.size == 6
20
+ until attempts.any? && (last_attempted_word == answer.join || attempts.size == 6)
21
+ attempts << make_attempt(answer)
22
+ end
17
23
 
18
- attempts.compact
24
+ last_attempted_word == answer.join
19
25
  end
20
26
 
21
- def make_attempt(answer, attempts)
22
- hits = []
23
- near_hits = []
24
- misses = []
25
-
26
- attempts.compact.each do |attempt|
27
- attempt.each_with_index do |letter, index|
28
- if letter == answer[index]
29
- hits[index] = letter
30
- elsif answer.include?(letter) && hits.count(letter) < answer.count(letter)
31
- near_hits[index] ||= []
32
- near_hits[index] << letter
33
- else
34
- misses << letter unless misses.include?(letter)
35
- end
27
+ def make_attempt(answer)
28
+ word = next_word
29
+ attempt = { word: word.join, result: [] }
30
+ word.each_with_index do |letter, index|
31
+ if letter == answer[index]
32
+ add_hit(letter, index, attempt)
33
+ elsif answer.include?(letter)
34
+ add_near_hit(letter, index, word, attempt)
35
+ else
36
+ add_miss(letter, attempt)
36
37
  end
37
38
  end
38
39
 
39
- find_potential_solution(hits, near_hits, misses)
40
+ attempt
41
+ end
42
+
43
+ def find_potential_solution
44
+ potential_solutions = Waylon::Wordle.vocabulary.map(&:chars).reject { _1.intersect?(misses) }
45
+
46
+ candidates = potential_solutions.select do |word|
47
+ hits_match?(hits, word) && near_hits_match?(near_hits, word)
48
+ end
49
+
50
+ candidates.shuffle.max_by { _1.uniq.size }
51
+ end
52
+
53
+ def last_attempted_word = attempts.last[:word]
54
+
55
+ private
56
+
57
+ def add_hit(letter, index, attempt)
58
+ hits[index] = letter
59
+ attempt[:result] << :hit
60
+ end
61
+
62
+ def add_near_hit(letter, index, word, attempt)
63
+ near_hits[index] ||= []
64
+ near_hits[index] << letter
65
+ attempt[:result] << (counts_as_near_hit?(word, index) ? :near_hit : :miss)
66
+ end
67
+
68
+ def add_miss(letter, attempt)
69
+ misses << letter unless misses.include?(letter)
70
+ attempt[:result] << :miss
71
+ end
72
+
73
+ def next_word = attempts.empty? ? startword.chars : find_potential_solution
74
+
75
+ def counts_as_near_hit?(word, index)
76
+ return false unless answer.include?(word[index])
77
+
78
+ answer_hit_count = answer.count(word[index])
79
+ word_hits = word.each_with_index.with_object([]) do |(letter, idx), indices|
80
+ indices << idx if letter == word[index] && answer[idx] == word[idx]
81
+ end
82
+
83
+ answer_hit_count > word_hits.size
40
84
  end
41
85
 
42
86
  def hits_match?(hits, exploded_word)
@@ -59,16 +103,6 @@ module Waylon
59
103
 
60
104
  true
61
105
  end
62
-
63
- def find_potential_solution(hits, near_hits, misses)
64
- potential_solutions = Waylon::Wordle.vocabulary.map(&:chars).reject do |word|
65
- word.intersect?(misses)
66
- end
67
-
68
- potential_solutions.select do |word|
69
- hits_match?(hits, word) && near_hits_match?(near_hits, word)
70
- end.sample
71
- end
72
106
  end
73
107
  end
74
108
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Waylon
4
4
  module Wordle
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.1"
6
6
  end
7
7
  end
data/lib/waylon/wordle.rb CHANGED
@@ -29,7 +29,13 @@ module Waylon
29
29
  end
30
30
 
31
31
  def self.random_startword
32
- vocabulary.select { |word| word.chars.intersection(%w[a e i o u]).uniq.size >= 3 }.sample
32
+ vocabulary.select do |word|
33
+ if rand(10) > 5
34
+ word.chars.uniq.intersection(%w[a e i o u]).uniq.size >= 4
35
+ else
36
+ word.chars.uniq.intersection(%w[a e o s t r n]).uniq.size >= 4
37
+ end
38
+ end.sample
33
39
  end
34
40
 
35
41
  def self.todays_date
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waylon-wordle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Gnagy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-30 00:00:00.000000000 Z
11
+ date: 2023-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: waylon-core