Glimmer Wordle 1.1.5 has just been released with official support for Windows. Although the game worked on Windows before, thanks to the platform-independent Glimmer DSL for SWT GUI Ruby Gem it was built with, styling was not tweaked for Windows till now in version 1.1.5.
For a reminder of how the game flows, here is an animated Gif of the game running on the Mac.
Here is the GUI View Ruby code using the Glimmer DSL for SWT Ruby Gem:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Source: https://github.com/AndyObtiva/glimmer_wordle | |
class Wordle | |
module View | |
class AppView | |
include Glimmer::UI::CustomShell | |
COLOR_TO_BACKGROUND_COLOR_MAP = { | |
green: rgb(106, 170, 100), | |
yellow: rgb(201, 180, 88), | |
gray: rgb(120, 124, 126), | |
} | |
COLOR_TO_TEXT_COLOR_MAP = { | |
green: :white, | |
yellow: :white, | |
gray: :white, | |
} | |
ALPHABET_LAYOUTS = { | |
alphabetical: [ | |
%w[A B C D E F G H I J], | |
%w[K L M N O P Q R S], | |
%w[T U V W X Y Z], | |
], | |
qwerty: [ | |
%w[Q W E R T Y U I O P], | |
%w[A S D F G H J K L], | |
%w[Z X C V B N M], | |
], | |
} | |
CONFIG_FILE = File.join(Dir.home, '.glimmer_wordle') | |
before_body do | |
@display = display { | |
on_about { | |
display_about_dialog | |
} | |
on_preferences { | |
display_about_dialog | |
} | |
on_swt_keydown do |key_event| | |
if key_event.keyCode == 8 | |
do_backspace | |
elsif key_event.keyCode == swt(:arrow_left) | |
do_left | |
elsif key_event.keyCode == swt(:arrow_right) | |
do_right | |
elsif key_event.keyCode == swt(:cr) | |
if @five_letter_word.status == :in_progress | |
do_guess | |
else | |
do_restart | |
end | |
elsif valid_character?((key_event.keyCode.chr rescue '')) | |
do_type(key_event.keyCode.chr) | |
end | |
end | |
} | |
@five_letter_word = Model::FiveLetterWord.new | |
config = load_config | |
@alphabet_layout = config[:alphabet_layout] || :alphabetical | |
end | |
## Add widget content inside custom shell body | |
## Top-most widget must be a shell or another custom shell | |
# | |
body { | |
shell(:no_resize) { | |
grid_layout { | |
margin_width 10 | |
margin_height 10 | |
vertical_spacing 0 | |
} | |
# Replace example content below with custom shell content | |
minimum_size 420, 540 | |
image File.join(APP_ROOT, 'icons', 'windows', "Glimmer Wordle.ico") if OS.windows? | |
image File.join(APP_ROOT, 'icons', 'linux', "Glimmer Wordle.png") unless OS.windows? | |
text "Glimmer Wordle" | |
background :white | |
app_menu_bar | |
alphabet_container | |
label { | |
layout_data :center, :center, true, false | |
text 'You have 6 tries to guess a 5-letter word' | |
background :transparent if OS.windows? | |
} | |
word_guesser | |
guess_button | |
} | |
} | |
def app_menu_bar | |
menu_bar { | |
menu { | |
text '&Game' | |
menu_item { | |
text '&Restart' | |
on_widget_selected { | |
do_restart | |
} | |
} | |
menu_item { | |
text 'E&xit' | |
on_widget_selected { | |
exit(0) | |
} | |
} | |
} | |
menu { | |
text '&View' | |
menu { | |
text 'Alphabet &Layout' | |
menu_item(:radio) { | |
text '&Alphabetical' | |
selection @alphabet_layout == :alphabetical | |
on_widget_selected { | |
self.alphabet_layout = :alphabetical | |
rebuild_alphabet_container | |
} | |
} | |
menu_item(:radio) { | |
text '&Qwerty' | |
selection @alphabet_layout == :qwerty | |
on_widget_selected { | |
self.alphabet_layout = :qwerty | |
rebuild_alphabet_container | |
} | |
} | |
} | |
} | |
menu { | |
text '&Help' | |
menu_item { | |
text '&Instructions' | |
on_widget_selected { | |
display_instructions_dialog | |
} | |
} | |
menu_item { | |
text '&About' | |
on_widget_selected { | |
display_about_dialog | |
} | |
} | |
} | |
} | |
end | |
def display_instructions_dialog | |
message_box(body_root) { | |
text 'Instructions' | |
message <<~MULTI_LINE_STRING | |
Make 6 guesses for a 5-letter word. | |
If you enter a letter that is part of the word, and at the right location, it becomes green, | |
If you enter a letter that is part of the word, but at the wrong location, it becomes yellow. | |
If you enter a letter that is not part of the word, it becomes red. | |
MULTI_LINE_STRING | |
}.open | |
end | |
def display_about_dialog | |
message_box(body_root) { | |
text 'About' | |
message "Glimmer Wordle #{VERSION}\n\n#{LICENSE}" | |
}.open | |
end | |
def alphabet_container | |
@alphabet_container = composite { | |
layout_data(:center, :center, true, false) | |
grid_layout { | |
margin_width 0 | |
margin_height 0 | |
vertical_spacing 0 | |
} | |
background :white | |
alphabets | |
} | |
end | |
def alphabets | |
alphabet_row(ALPHABET_LAYOUTS[@alphabet_layout][0]) { | |
layout_data(:center, :center, true, false) { | |
width_hint 318 | |
height_hint 50 | |
} | |
} | |
alphabet_row(ALPHABET_LAYOUTS[@alphabet_layout][1]) { | |
layout_data(:center, :center, true, false) { | |
width_hint 288 | |
height_hint 50 | |
} | |
} | |
alphabet_row(ALPHABET_LAYOUTS[@alphabet_layout][2]) { | |
layout_data(:center, :center, true, false) { | |
width_hint 222 | |
height_hint 50 | |
} | |
} | |
end | |
def alphabet_row(alphabet_characters, &block) | |
canvas { | |
block.call | |
background :white | |
@alphabet_rectangles ||= [] | |
@alphabet_borders ||= [] | |
@alphabet_letters ||= [] | |
alphabet_characters.each_with_index do |alphabet_character, i| | |
@alphabet_rectangles << rectangle(1 + i*32, @alphabet_row_offset_y, 28, 28) { | |
background :transparent | |
@alphabet_borders << rectangle { | |
foreground :gray | |
line_width 2 | |
} | |
@alphabet_letters << text(alphabet_character, :default, [:default, OS.linux? ? 5 : (OS.windows? ? 1 : 0)]) { | |
font alphabet_font | |
} | |
} | |
end | |
} | |
end | |
def alphabet_layout_alphabets | |
ALPHABET_LAYOUTS[@alphabet_layout].reduce(:+) | |
end | |
def word_guesser | |
@canvasses ||= [] | |
margin_x = 5 | |
margin_y = 5 | |
@canvasses << canvas { | |
layout_data(:center, :center, true, false) { | |
width_hint 230 | |
height_hint 50 | |
} | |
background :white | |
focus true | |
@rectangles = [] | |
@borders = [] | |
@letters = [] | |
5.times do |i| | |
@rectangles << rectangle(margin_x + i*45, margin_y, 40, 40) { | |
background :transparent | |
@borders << rectangle { | |
foreground i == 0 ? :title_background : :gray | |
line_width 2 | |
} | |
@letters << text('', :default, [:default, OS.linux? ? 6 : (OS.windows? ? 1 : 0)]) { | |
font letter_font | |
} | |
} | |
end | |
} | |
end | |
def guess_button | |
@guess_button = button { | |
layout_data :center, :center, true, false | |
text 'Guess' | |
on_widget_selected do | |
do_guess | |
end | |
} | |
end | |
def do_backspace | |
@letter = @letters[highlighted_letter_index] | |
if @letter.string != '' | |
index_to_delete = highlighted_letter_index | |
else | |
index_to_delete = [highlighted_letter_index - 1, 0].max | |
end | |
@letter = @letters[index_to_delete] | |
@letter.string = '' | |
@borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret | |
@borders[index_to_delete].foreground = :title_background | |
end | |
def do_guess | |
return if !word_filled_up? | |
word = @letters.map(&:string).join | |
if invalid_word?(word) | |
message_box { | |
text 'Invalid Word' | |
message "The word you entered is not an allowed guess!\n\nPlease try another word!" | |
}.open | |
return | |
end | |
guess_result = @five_letter_word.guess(word) | |
update_guess_word_background_colors(guess_result) | |
update_alphabet_background_colors | |
if @five_letter_word.status == :in_progress | |
@guess_button.dispose | |
body_root.content { | |
word_guesser | |
guess_button | |
} | |
else | |
@guess_button.dispose | |
body_root.content { | |
@restart_button = button { | |
layout_data :center, :center, true, false | |
text 'Restart' | |
focus true | |
on_widget_selected do | |
do_restart | |
end | |
} | |
} | |
display_share_text_dialog | |
end | |
body_root.layout(true, true) | |
body_root.pack(true) | |
end | |
def highlighted_letter_index | |
@borders.each_with_index.find {|border, i| border.foreground.first == color(:title_background).swt_color }.last | |
end | |
def do_type(character) | |
index = highlighted_letter_index | |
@letter = @letters[index] | |
@letter.string = character.upcase | |
if @letters.any? {|letter| letter.string == ''} | |
@borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret | |
@borders[index == 4 ? 4 : index + 1].foreground = :title_background | |
end | |
end | |
def do_left | |
index = [highlighted_letter_index - 1, 0].max | |
@borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret | |
@borders[index].foreground = :title_background | |
end | |
def do_right | |
index = [highlighted_letter_index + 1, @letters.count - 1].min | |
@borders.each { |caret| caret.foreground = :gray} # refactor this reusable code into a method that highlights the caret | |
@borders[index].foreground = :title_background | |
end | |
def do_restart | |
@share_text_dialog&.close | |
alphabet_layout_alphabets.each_with_index do |alphabet_character, i| | |
@alphabet_borders[i].foreground = :gray | |
@alphabet_rectangles[i].background = :white | |
@alphabet_letters[i].foreground = :black | |
end | |
@restart_button&.dispose | |
@canvasses.dup.each(&:dispose) | |
@canvasses.clear | |
@guess_button&.dispose | |
body_root.content { | |
word_guesser | |
guess_button | |
} | |
body_root.layout(true, true) | |
body_root.pack(true) | |
@five_letter_word.refresh | |
end | |
def update_guess_word_background_colors(guess_result) | |
guess_result.each_with_index do |result_color, i| | |
background_color = COLOR_TO_BACKGROUND_COLOR_MAP[result_color] | |
@borders[i].foreground = background_color | |
@rectangles[i].background = background_color | |
@letters[i].foreground = COLOR_TO_TEXT_COLOR_MAP[result_color] | |
async_exec { @canvasses.last.redraw } | |
end | |
end | |
def update_alphabet_background_colors | |
alphabet_layout_alphabets.each_with_index do |alphabet_character, i| | |
result_color = @five_letter_word.colored_alphabets[alphabet_character.downcase] | |
if result_color | |
background_color = COLOR_TO_BACKGROUND_COLOR_MAP[result_color] | |
@alphabet_borders[i].foreground = background_color | |
@alphabet_rectangles[i].background = background_color | |
@alphabet_letters[i].foreground = COLOR_TO_TEXT_COLOR_MAP[result_color] | |
end | |
end | |
end | |
def word_filled_up? | |
@letters.find {|letter| letter.string == ''}.nil? | |
end | |
def invalid_word?(word) | |
!Model::FiveLetterWord::WORLD_ALLOWED_GUESSES.include?(word.downcase) | |
end | |
def valid_character?(character) | |
((65..90).to_a + (97..122).to_a).map {|n| n.chr}.include?(character) | |
end | |
def display_share_text_dialog | |
result = "#{@five_letter_word.answer.upcase}\n\n#{emoji_result}" | |
Clipboard.copy(result) | |
@share_text_dialog = dialog(body_root) { | |
grid_layout | |
text 'Share Result' | |
label { | |
layout_data :center, :center, true, false | |
text 'Result is copied to clipboard!' | |
} | |
styled_text { | |
layout_data :fill, :fill, true, true | |
editable false | |
caret nil | |
alignment :center | |
font name: 'Segoe UI Emoji' if OS.windows? | |
text result | |
} | |
} | |
@share_text_dialog.open | |
end | |
def alphabet_font | |
the_font = {style: :bold, height: 28} | |
the_font.merge!(name: 'Helvetica', height: 21) if OS.linux? | |
the_font.merge!(name: 'Arial', height: 25) if OS.windows? | |
the_font | |
end | |
def letter_font | |
the_font = {style: :bold, height: 40} | |
the_font.merge!(name: 'Helvetica', height: 30) if OS.linux? | |
the_font.merge!(name: 'Arial', height: 32) if OS.windows? | |
the_font | |
end | |
def dispose_alphabet_container_children | |
@alphabet_rectangles.clear | |
@alphabet_borders.clear | |
@alphabet_letters.clear | |
@alphabet_container.children.each(&:dispose) | |
end | |
def rebuild_alphabet_container | |
dispose_alphabet_container_children | |
@alphabet_container.content { | |
alphabets | |
} | |
@alphabet_container.layout(true, true) | |
@alphabet_container.pack(true) | |
update_alphabet_background_colors | |
end | |
def alphabet_layout=(value) | |
@alphabet_layout = value | |
save_config | |
end | |
def new_config | |
{ | |
alphabet_layout: @alphabet_layout | |
} | |
end | |
def save_config | |
File.write(CONFIG_FILE, YAML.dump(new_config)) | |
rescue => e | |
puts e.full_message | |
end | |
def load_config | |
File.exist?(CONFIG_FILE) ? YAML.load(File.read(CONFIG_FILE)) : {} | |
rescue => e | |
puts e.full_message | |
{} | |
end | |
def emoji_result | |
result = '' | |
@five_letter_word.guess_results.each do |row| | |
row.each do |result_color| | |
case result_color | |
when :green | |
result << "🟩" | |
when :yellow | |
result << "🟨" | |
when :gray | |
result << "⬜" | |
end | |
end | |
result << "\n" | |
end | |
result | |
end | |
end | |
end | |
end |
Happy gaming!
No comments:
Post a Comment