fix: show rules and explain mistakes

This commit is contained in:
Glenn Y. Rolland 2025-06-14 15:49:42 +02:00
parent 9de046b8ac
commit 3c19338971
6 changed files with 47 additions and 41 deletions

Binary file not shown.

View file

@ -1,32 +1,33 @@
class Bordle
class Bordle
class Dictionary class Dictionary
DICTIONARY_FILE = "/usr/share/dict/french" DICTIONARY_PATH = Path.new("/usr/share/dict/")
property length : UInt8 property length : UInt8
property data : Array(String) property data : Array(String)
property language : String = "french"
def initialize(length : UInt8) def initialize(length : UInt8)
@length = length @length = length
@data = [] of String @data = [] of String
load_data load_data
end end
def load_data def load_data
dictionary_file = DICTIONARY_PATH / @language
# use french dictionary from wfrench package # use french dictionary from wfrench package
if ! File.exists? DICTIONARY_FILE if !File.exists? dictionary_file
STDERR.puts "ERROR: dictionary file missing! (#{DICTIONARY_FILE})" STDERR.puts "ERROR: dictionary file missing! (#{dictionary_file})"
STDERR.puts " Please install then wfrench package on your system" STDERR.puts " Please install then w#{@language} package on your system"
exit 1 exit 1
end end
lines = File.read_lines(DICTIONARY_FILE) lines = File.read_lines(dictionary_file)
@data = @data =
lines lines
.select {|word| word.size == @length } .select { |word| word.size == @length }
.map { |word| word.tr( TR_DIACRITICS, TR_ASCII ) } .map { |word| word.tr(TR_DIACRITICS, TR_ASCII) }
end end
def includes?(word) def includes?(word)
@data.includes? word @data.includes? word
end end

View file

@ -1,11 +1,10 @@
require "colorize" require "colorize"
require "./types" require "./types"
require "./dictionary" require "./dictionary"
require "./target_word" require "./target_word"
class Bordle class Bordle
class Game class Game
def initialize def initialize
@length = 5_u8 @length = 5_u8
@ -17,10 +16,10 @@ class Bordle
printf("#{try}. ") printf("#{try}. ")
diff.each do |ls| diff.each do |ls|
colors = case ls[1] colors = case ls[1]
when Score::NotOk then {:white, :black} when Score::NotOk then {:white, :black}
when Score::WrongPlace then {:white, :yellow} when Score::WrongPlace then {:white, :yellow}
when Score::RightPlace then {:white, :green} when Score::RightPlace then {:white, :green}
else {:white, :black} else {:white, :black}
end end
str = ("%s" % ls[0]).colorize.fore(colors[0]).back(colors[1]) str = ("%s" % ls[0]).colorize.fore(colors[0]).back(colors[1])
printf("%s", str) printf("%s", str)
@ -37,16 +36,30 @@ class Bordle
word = "" if word.nil? word = "" if word.nil?
word.tr(TR_DIACRITICS, TR_ASCII).downcase word.tr(TR_DIACRITICS, TR_ASCII).downcase
error = [] of String
error << "language" if !@dict.includes? word
error << "length" if word.size != @length
break if word.size == @length && @dict.includes? word break if word.size == @length && @dict.includes? word
printf("\x1B[A\x1B[2K") printf("\x1B[A\x1B[2K")
puts "-- #{word} : invalid #{error.join(" and ")} --"
end end
word word
end end
def run def run
printf " .....\n" puts "-- BORDLE (#{@target.inspect}) --".colorize.fore(:blue)
puts <<-MARK
1. You win when you find the secret word.
2. You have 5 attempts; you lose if you use them all.
3. The secret word is in #{@dict.language} and 5 characters long.
4. Language and length mistakes are not counted as attemps.
5. Diacritics (accents, etc.) are removed.
6. Use CTRL-C to exit.
MARK
puts ""
puts " ....."
try = 0 try = 0
while true while true
try += 1 try += 1
word = input_word(try) word = input_word(try)
printf("\x1B[A\x1B[2K") printf("\x1B[A\x1B[2K")
@ -57,7 +70,7 @@ class Bordle
puts "-- GAGNÉ ! --".colorize.fore(:green) puts "-- GAGNÉ ! --".colorize.fore(:green)
return return
end end
if try >= 5 if try >= 5
puts "-- PERDU ! --".colorize.fore(:red) puts "-- PERDU ! --".colorize.fore(:red)
return return
end end
@ -65,4 +78,3 @@ class Bordle
end end
end end
end end

View file

@ -1,14 +1,12 @@
require "option_parser" require "option_parser"
require "./game" require "./game"
class Bordle class Bordle
class BordleCli class BordleCli
property options : String? property options : String?
def initialize() def initialize
@options = nil @options = nil
end end
@ -17,20 +15,18 @@ class Bordle
end end
# FIXME: add --length LEN option (length of words) # FIXME: add --length LEN option (length of words)
# FIXME: add --lang LANG option (choose dictionnary) # FIXME: add --language LANG option (choose dictionnary)
# FIXME: add --tries TRIES option (how many tries are allowed) # FIXME: add --tries TRIES option (how many tries are allowed)
# FIXME: add --with-letters (show used/unused letters) # FIXME: add --with-letters (show used/unused letters)
def self.run(args) def self.run(args)
app = BordleCli.new app = BordleCli.new
options = BordleCli.parse_options(args) options = BordleCli.parse_options(args)
app.options = options app.options = options
game = Game.new game = Game.new
game.run() game.run
end end
end end
end end
Bordle::BordleCli.run(ARGV) Bordle::BordleCli.run(ARGV)

View file

@ -1,5 +1,4 @@
class Bordle
class Bordle
class TargetWord class TargetWord
def initialize(@target_word : String) def initialize(@target_word : String)
end end
@ -8,7 +7,7 @@ class Bordle
@target_word == word @target_word == word
end end
def to_h() def to_h
hash = Hash(Char, Array(Int32)).new hash = Hash(Char, Array(Int32)).new
@target_word.each_char_with_index do |char, index| @target_word.each_char_with_index do |char, index|
hash[char] = [] of Int32 unless hash.has_key? char hash[char] = [] of Int32 unless hash.has_key? char
@ -22,30 +21,30 @@ class Bordle
result = [] of LetterScore result = [] of LetterScore
# REF = r a t e s # REF = r a t e s
# TEST= c e r e t # TEST= c e r e t
word.each_char_with_index do |char, index| word.each_char_with_index do |char, index|
if ! hash.has_key? char if !hash.has_key? char
result << {char, Score::NotOk} result << {char, Score::NotOk}
next next
end end
char_values = hash[char] char_values = hash[char]
char_misplaced = char_values.select {|pos| @target_word[pos] != word[pos] } char_misplaced = char_values.select { |pos| @target_word[pos] != word[pos] }
char_wellplaced = char_values.select {|pos| @target_word[pos] == word[pos] } char_wellplaced = char_values.select { |pos| @target_word[pos] == word[pos] }
# puts "values(#{char}) = #{char_values}" # puts "values(#{char}) = #{char_values}"
# puts "misplaced(#{char}) = #{char_misplaced}" # puts "misplaced(#{char}) = #{char_misplaced}"
# puts "wellplaced(#{char}) = #{char_wellplaced}" # puts "wellplaced(#{char}) = #{char_wellplaced}"
if char_wellplaced.includes? index if char_wellplaced.includes? index
result << {char, Score::RightPlace} result << {char, Score::RightPlace}
char_wellplaced.reject! {|pos| pos == index } char_wellplaced.reject! { |pos| pos == index }
hash[char] = char_misplaced + char_wellplaced hash[char] = char_misplaced + char_wellplaced
hash.delete(char) if hash[char].empty? hash.delete(char) if hash[char].empty?
next next
end end
if ! char_misplaced.empty? if !char_misplaced.empty?
result << {char, Score::WrongPlace} result << {char, Score::WrongPlace}
char_misplaced = char_misplaced.skip(1) char_misplaced = char_misplaced.skip(1)
hash[char] = char_misplaced + char_wellplaced hash[char] = char_misplaced + char_wellplaced

View file

@ -1,8 +1,6 @@
class Bordle
class Bordle
TR_DIACRITICS = "ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšȘșſŢţŤťŦŧȚțÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž" TR_DIACRITICS = "ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšȘșſŢţŤťŦŧȚțÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž"
TR_ASCII = "AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSsSssTtTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz" TR_ASCII = "AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSsSssTtTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz"
enum Score enum Score
RightPlace = 0 RightPlace = 0