mirror of
https://github.com/pragma-/pbot.git
synced 2025-12-23 11:28:12 +01:00
Plugin/Wordle: add words remaining counter + multiple improvements
* hard mode enabled by default * fixed several harmless uninit warnings * other minor improvements
This commit is contained in:
parent
599ca628d5
commit
ede9260676
@ -36,7 +36,7 @@ sub unload($self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use constant {
|
use constant {
|
||||||
USAGE => 'Usage: wordle start [length [wordlist [game-id]]] | custom <word> <channel> [wordlist [game-id]] | guess <word> [game-id] | select [game-id] | list | guesses [game-id] | letters [game-id] | show [game-id] | info [game-id] | hard [on|off|status [game-id]] | giveup [game-id]',
|
USAGE => 'Usage: wordle start [length [wordlist [game-id]]] | custom <word> <channel> [wordlist [game-id]] | guess <word> [game-id] | select [game-id] | list | guesses [game-id] | letters [game-id] | show [game-id] | info [game-id] | players | hard [on|off|status [game-id]] | giveup [game-id]',
|
||||||
|
|
||||||
NO_WORDLE => 'There is no Wordle yet. Use `wordle start` to begin a game.',
|
NO_WORDLE => 'There is no Wordle yet. Use `wordle start` to begin a game.',
|
||||||
NO_GAMEID => 'That game-id does not exist. Use `wordle start <length> <wordlist> <gameid>` to begin a game with that id.',
|
NO_GAMEID => 'That game-id does not exist. Use `wordle start <length> <wordlist> <gameid>` to begin a game with that id.',
|
||||||
@ -214,12 +214,16 @@ sub wordle($self, $context) {
|
|||||||
|
|
||||||
my @games;
|
my @games;
|
||||||
foreach my $gameid (keys $self->{games}->{$channel}->%*) {
|
foreach my $gameid (keys $self->{games}->{$channel}->%*) {
|
||||||
|
if (not exists $self->{games}->{$channel}->{$gameid}->{wordle}) {
|
||||||
|
push @games, "$gameid (none)";
|
||||||
|
} else {
|
||||||
my $length = $self->{games}->{$channel}->{$gameid}->{length};
|
my $length = $self->{games}->{$channel}->{$gameid}->{length};
|
||||||
my $wordlist = $self->{games}->{$channel}->{$gameid}->{wordlist};
|
my $wordlist = $self->{games}->{$channel}->{$gameid}->{wordlist};
|
||||||
my $solved = $self->{games}->{$channel}->{$gameid}->{solved} ? ', solved' : '';
|
my $solved = $self->{games}->{$channel}->{$gameid}->{solved} ? ', solved' : '';
|
||||||
my $givenup = $self->{games}->{$channel}->{$gameid}->{givenup} ? ', given up' : '';
|
my $givenup = $self->{games}->{$channel}->{$gameid}->{givenup} ? ', given up' : '';
|
||||||
push @games, "$gameid ($wordlist:$length$solved$givenup)";
|
push @games, "$gameid ($wordlist:$length$solved$givenup)";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (not @games) {
|
if (not @games) {
|
||||||
push @games, 'none';
|
push @games, 'none';
|
||||||
@ -234,7 +238,7 @@ sub wordle($self, $context) {
|
|||||||
return "Usage: wordle select [game-id]";
|
return "Usage: wordle select [game-id]";
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($gameid, $error) = $self->gameid($args[0], $context);
|
my ($gameid, $error) = $self->gameid($args[0], $context, 1);
|
||||||
|
|
||||||
if (defined $error) {
|
if (defined $error) {
|
||||||
return $error;
|
return $error;
|
||||||
@ -244,10 +248,33 @@ sub wordle($self, $context) {
|
|||||||
return "$context->{nick}: You are playing the $gameid Wordle.";
|
return "$context->{nick}: You are playing the $gameid Wordle.";
|
||||||
} else {
|
} else {
|
||||||
$self->{players}->{$channel}->{$context->{message_account}}->{gameid} = $gameid;
|
$self->{players}->{$channel}->{$context->{message_account}}->{gameid} = $gameid;
|
||||||
|
$self->{games}->{$channel}->{$gameid}->{exists} = 1;
|
||||||
return "$context->{nick} is now playing the $gameid Wordle!";
|
return "$context->{nick} is now playing the $gameid Wordle!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (isabbrev($_, 'state')) {
|
||||||
|
if (@args > 1) {
|
||||||
|
return "Usage: wordle state [game-id]";
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($gameid, $error) = $self->gameid($args[0], $context);
|
||||||
|
|
||||||
|
if (defined $error) {
|
||||||
|
return $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $self->guesses2state($channel, $gameid);
|
||||||
|
}
|
||||||
|
|
||||||
|
when (isabbrev($_, 's2p')) {
|
||||||
|
if (@args != 1) {
|
||||||
|
return "Usage: wordle s2p <state>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $self->state2pattern($args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
when (isabbrev($_, 'info')) {
|
when (isabbrev($_, 'info')) {
|
||||||
if (@args > 2) {
|
if (@args > 2) {
|
||||||
return "Usage: wordle info [game-id]";
|
return "Usage: wordle info [game-id]";
|
||||||
@ -336,6 +363,45 @@ sub wordle($self, $context) {
|
|||||||
return $game . $result;
|
return $game . $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (isabbrev($_, 'guess')) {
|
||||||
|
if (!@args || @args > 2) {
|
||||||
|
return "Usage: wordle guess <word> [game-id]";
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($gameid, $error) = $self->gameid($args[1], $context);
|
||||||
|
|
||||||
|
if (defined $error) {
|
||||||
|
return $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $game = $gameid ne 'main' ? "($gameid) " : '';
|
||||||
|
|
||||||
|
if (!exists $self->{games}->{$channel}->{$gameid} || !defined $self->{games}->{$channel}->{$gameid}->{wordle}) {
|
||||||
|
return $game . NO_WORDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists $self->{players}->{$channel} || !exists $self->{players}->{$channel}->{$context->{message_account}}) {
|
||||||
|
$self->{players}->{$channel}->{$context->{message_account}}->{gameid} = $gameid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($self->{games}->{$channel}->{$gameid}->{solved}) {
|
||||||
|
return "${game}Wordle already solved by $self->{games}->{$channel}->{$gameid}->{solved_by}. " . $self->show_wordle($channel, $gameid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($self->{games}->{$channel}->{$gameid}->{givenup}) {
|
||||||
|
return "${game}Wordle given up by $self->{games}->{$channel}->{$gameid}->{givenup_by}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $result = $game . $self->guess_wordle($channel, $args[0], $gameid);
|
||||||
|
|
||||||
|
if ($self->{games}->{$channel}->{$gameid}->{solved}) {
|
||||||
|
$self->{games}->{$channel}->{$gameid}->{solved_by} = $context->{nick};
|
||||||
|
$self->{games}->{$channel}->{$gameid}->{solved_on} = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
when (isabbrev($_, 'giveup')) {
|
when (isabbrev($_, 'giveup')) {
|
||||||
if (@args > 1) {
|
if (@args > 1) {
|
||||||
return "Usage: wordle giveup [game-id]";
|
return "Usage: wordle giveup [game-id]";
|
||||||
@ -479,45 +545,6 @@ sub wordle($self, $context) {
|
|||||||
return "Custom Wordle started!";
|
return "Custom Wordle started!";
|
||||||
}
|
}
|
||||||
|
|
||||||
when (isabbrev($_, 'guess')) {
|
|
||||||
if (!@args || @args > 2) {
|
|
||||||
return "Usage: wordle guess <word> [game-id]";
|
|
||||||
}
|
|
||||||
|
|
||||||
my ($gameid, $error) = $self->gameid($args[1], $context);
|
|
||||||
|
|
||||||
if (defined $error) {
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $game = $gameid ne 'main' ? "($gameid) " : '';
|
|
||||||
|
|
||||||
if (!exists $self->{games}->{$channel}->{$gameid} || !defined $self->{games}->{$channel}->{$gameid}->{wordle}) {
|
|
||||||
return $game . NO_WORDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exists $self->{players}->{$channel} || !exists $self->{players}->{$channel}->{$context->{message_account}}) {
|
|
||||||
$self->{players}->{$channel}->{$context->{message_account}}->{gameid} = $gameid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($self->{games}->{$channel}->{$gameid}->{solved}) {
|
|
||||||
return "${game}Wordle already solved by $self->{games}->{$channel}->{$gameid}->{solved_by}. " . $self->show_wordle($channel, $gameid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($self->{games}->{$channel}->{$gameid}->{givenup}) {
|
|
||||||
return "${game}Wordle given up by $self->{games}->{$channel}->{$gameid}->{givenup_by}.";
|
|
||||||
}
|
|
||||||
|
|
||||||
my $result = $game . $self->guess_wordle($channel, $args[0], $gameid);
|
|
||||||
|
|
||||||
if ($self->{games}->{$channel}->{$gameid}->{solved}) {
|
|
||||||
$self->{games}->{$channel}->{$gameid}->{solved_by} = $context->{nick};
|
|
||||||
$self->{games}->{$channel}->{$gameid}->{solved_on} = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
when (isabbrev($_, 'hard')) {
|
when (isabbrev($_, 'hard')) {
|
||||||
my ($gameid, $error) = $self->gameid($args[1], $context);
|
my ($gameid, $error) = $self->gameid($args[1], $context);
|
||||||
|
|
||||||
@ -725,7 +752,7 @@ sub make_wordle($self, $nick, $channel, $length, $gameid = 'main', $word = undef
|
|||||||
$self->{games}->{$channel}->{$gameid}->{start_nick} = $nick;
|
$self->{games}->{$channel}->{$gameid}->{start_nick} = $nick;
|
||||||
|
|
||||||
if (not defined $self->{games}->{$channel}->{$gameid}->{hard_mode}) {
|
if (not defined $self->{games}->{$channel}->{$gameid}->{hard_mode}) {
|
||||||
$self->{games}->{$channel}->{$gameid}->{hard_mode} = 0;
|
$self->{games}->{$channel}->{$gameid}->{hard_mode} = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $letter ('A'..'Z') {
|
foreach my $letter ('A'..'Z') {
|
||||||
@ -767,7 +794,23 @@ sub show_letters($self, $channel, $gameid = 'main') {
|
|||||||
|
|
||||||
sub show_wordle($self, $channel, $gameid = 'main', $with_letters = 0) {
|
sub show_wordle($self, $channel, $gameid = 'main', $with_letters = 0) {
|
||||||
if ($with_letters) {
|
if ($with_letters) {
|
||||||
return $self->{games}->{$channel}->{$gameid}->{guess} . "$color{reset} " . $self->show_letters($channel, $gameid);
|
my $state = $self->guesses2state($channel, $gameid);
|
||||||
|
my $pattern = $self->state2pattern($state);
|
||||||
|
|
||||||
|
my @words;
|
||||||
|
if (defined $pattern) {
|
||||||
|
@words = $self->pattern2words($channel, $gameid, $pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $count = @words;
|
||||||
|
|
||||||
|
if ($count) {
|
||||||
|
$count = "($count word" . ($count == 1 ? '' : 's') . ')';
|
||||||
|
} else {
|
||||||
|
$count = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $self->{games}->{$channel}->{$gameid}->{guess} . "$color{reset} " . $self->show_letters($channel, $gameid) . $count;
|
||||||
} else {
|
} else {
|
||||||
return $self->{games}->{$channel}->{$gameid}->{guess} . "$color{reset}";
|
return $self->{games}->{$channel}->{$gameid}->{guess} . "$color{reset}";
|
||||||
}
|
}
|
||||||
@ -808,7 +851,7 @@ sub guess_wordle($self, $channel, $guess, $gameid = 'main') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach my $orange ($self->{games}->{$channel}->{$gameid}->{oranges}->@*) {
|
foreach my $orange ($self->{games}->{$channel}->{$gameid}->{oranges}->@*) {
|
||||||
if ($guess[$i] eq $orange->[$i]) {
|
if (defined $orange->[$i] && $guess[$i] eq $orange->[$i]) {
|
||||||
$self->{games}->{$channel}->{$gameid}->{invalid_count}++;
|
$self->{games}->{$channel}->{$gameid}->{invalid_count}++;
|
||||||
return "Hard mode is enabled. Position " . ($i + 1) . " can't be $guess[$i]. Try again.";
|
return "Hard mode is enabled. Position " . ($i + 1) . " can't be $guess[$i]. Try again.";
|
||||||
}
|
}
|
||||||
@ -824,7 +867,7 @@ sub guess_wordle($self, $channel, $guess, $gameid = 'main') {
|
|||||||
foreach my $o (keys %oranges) {
|
foreach my $o (keys %oranges) {
|
||||||
my $count = 0;
|
my $count = 0;
|
||||||
$_ eq $o && $count++ foreach @guess;
|
$_ eq $o && $count++ foreach @guess;
|
||||||
if ($count < $oranges{$o} + $greens{$o}) {
|
if ($count < ($oranges{$o} // 0) + ($greens{$o} // 0)) {
|
||||||
$self->{games}->{$channel}->{$gameid}->{invalid_count}++;
|
$self->{games}->{$channel}->{$gameid}->{invalid_count}++;
|
||||||
return "Hard mode is enabled. There must be " . ($oranges{$o} + $greens{$o}) . " $o. Try again.";
|
return "Hard mode is enabled. There must be " . ($oranges{$o} + $greens{$o}) . " $o. Try again.";
|
||||||
}
|
}
|
||||||
@ -833,13 +876,13 @@ sub guess_wordle($self, $channel, $guess, $gameid = 'main') {
|
|||||||
|
|
||||||
foreach my $white ($self->{games}->{$channel}->{$gameid}->{whites}->@*) {
|
foreach my $white ($self->{games}->{$channel}->{$gameid}->{whites}->@*) {
|
||||||
for (my $i = 0; $i < @guess; $i++) {
|
for (my $i = 0; $i < @guess; $i++) {
|
||||||
if ($guess[$i] eq $white->[$i]) {
|
if (defined $white->[$i] && $guess[$i] eq $white->[$i]) {
|
||||||
$self->{games}->{$channel}->{$gameid}->{invalid_count}++;
|
$self->{games}->{$channel}->{$gameid}->{invalid_count}++;
|
||||||
return "Hard mode is enabled. Position " . ($i + 1) . " can't be $guess[$i]. Try again.";
|
return "Hard mode is enabled. Position " . ($i + 1) . " can't be $guess[$i]. Try again.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not $self->{games}->{$channel}->{$gameid}->{letter_max}->{$white->[$i]}) {
|
if (defined $white->[$i] && !$self->{games}->{$channel}->{$gameid}->{letter_max}->{$white->[$i]}) {
|
||||||
my $count = $greens{$white->[$i]} + $oranges{$white->[$i]};
|
my $count = ($greens{$white->[$i]} // 0) + ($oranges{$white->[$i]} // 0);
|
||||||
|
|
||||||
if ($count) {
|
if ($count) {
|
||||||
$self->{games}->{$channel}->{$gameid}->{letter_max}->{$white->[$i]} = $count;
|
$self->{games}->{$channel}->{$gameid}->{letter_max}->{$white->[$i]} = $count;
|
||||||
@ -961,11 +1004,143 @@ sub guess_wordle($self, $channel, $guess, $gameid = 'main') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# inspired by Just Another Archivist's jaa-guesses2state factoid
|
||||||
|
sub guesses2state($self, $channel, $gameid) {
|
||||||
|
my %modifier = ('*' => 'G', '?' => 'Y', ' ' => 'B');
|
||||||
|
|
||||||
|
my @results;
|
||||||
|
|
||||||
|
foreach my $guess ($self->{games}->{$channel}->{$gameid}->{guesses}->@*) {
|
||||||
|
my $word = $guess;
|
||||||
|
$word =~ s/[^\pL]//g;
|
||||||
|
|
||||||
|
my $result = lc "$word.";
|
||||||
|
|
||||||
|
my $g = $guess;
|
||||||
|
$g =~ s/\x02|\x0F|\x11|\x1D|\x1E|\x1F|\x03([0-9][0-9]?(,[0-9][0-9]?)?)?//g;
|
||||||
|
|
||||||
|
my @states = ($g =~ /..(.)/g);
|
||||||
|
foreach my $state (@states) {
|
||||||
|
$result .= $modifier{$state};
|
||||||
|
}
|
||||||
|
|
||||||
|
push @results, $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return join '.', @results;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ported from Just Another Archivist's jaasolve-state2pattern factoid
|
||||||
|
sub state2pattern($self, $state) {
|
||||||
|
my ($guess, $result, %min, %exact, %missing, %letters, @pospattern, $char, $rchar);
|
||||||
|
|
||||||
|
($result) = $state =~ m/([^\.]+)/;
|
||||||
|
my $length = length $result;
|
||||||
|
|
||||||
|
while ($state) {
|
||||||
|
($guess, $result, $state) = split /\./, $state, 3;
|
||||||
|
|
||||||
|
if (length $guess != length $result || length $guess != $length) {
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
%letters = ();
|
||||||
|
|
||||||
|
for (my $i = 0; $i < $length; ++$i) {
|
||||||
|
$char = substr($guess, $i, 1);
|
||||||
|
$rchar = substr($result, $i, 1);
|
||||||
|
|
||||||
|
if ($rchar eq 'G') {
|
||||||
|
$pospattern[$i] = $char;
|
||||||
|
$letters{$char}++;
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif ($rchar eq 'Y') {
|
||||||
|
if ($pospattern[$i] !~ /^[^^]/) {
|
||||||
|
$pospattern[$i] = ($pospattern[$i] ? $pospattern[$i] : '^') . $char;
|
||||||
|
}
|
||||||
|
$letters{$char}++;
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif ($rchar eq 'B') {
|
||||||
|
if ($pospattern[$i] !~ /^[^^]/) {
|
||||||
|
$pospattern[$i] = ($pospattern[$i] ? $pospattern[$i] : '^') . $char;
|
||||||
|
}
|
||||||
|
$missing{$char} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach $char (keys %letters) {
|
||||||
|
if ($letters{$char} > ($min{$char} // 0)) {
|
||||||
|
$min{$char} = $letters{$char};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach $char (keys %missing) {
|
||||||
|
if ($min{$char}) {
|
||||||
|
$exact{$char} = $min{$char};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach $char (keys %missing) {
|
||||||
|
if ($min{$char}) {
|
||||||
|
delete $missing{$char};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $pattern = '^';
|
||||||
|
|
||||||
|
if (%missing) {
|
||||||
|
$pattern .= '(?!.*[' . (join '', keys %missing) . '])';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (%min) {
|
||||||
|
foreach $char (keys %min) {
|
||||||
|
$pattern .= '(?=';
|
||||||
|
for (my $i = 0; $i < $min{$char}; ++$i) {
|
||||||
|
$pattern .= ".*$char";
|
||||||
|
}
|
||||||
|
$pattern .= ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (%exact) {
|
||||||
|
foreach $char (keys %exact) {
|
||||||
|
$pattern .= '(?!';
|
||||||
|
for (my $i = 0; $i < $exact{$char} + 1; ++$i) {
|
||||||
|
$pattern .= ".*$char";
|
||||||
|
}
|
||||||
|
$pattern .= ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $subpattern;
|
||||||
|
for (my $i = 0; $i < $length; ++$i) {
|
||||||
|
$subpattern = $pospattern[$i];
|
||||||
|
|
||||||
|
if (defined $subpattern && $subpattern =~ /^\^/) {
|
||||||
|
$pattern .= "[$subpattern]";
|
||||||
|
} else {
|
||||||
|
$pattern .= (length $subpattern ? $subpattern : '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pattern .= '$';
|
||||||
|
return $pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub pattern2words($self, $channel, $gameid, $pattern) {
|
||||||
|
my @keys = keys $self->{games}->{$channel}->{$gameid}->{words}->%*;
|
||||||
|
return grep { /$pattern/i } @keys;
|
||||||
|
}
|
||||||
|
|
||||||
sub check_games($self) {
|
sub check_games($self) {
|
||||||
my $now = time;
|
my $now = time;
|
||||||
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
||||||
foreach my $channel (keys $self->{games}->%*) {
|
foreach my $channel (keys $self->{games}->%*) {
|
||||||
foreach my $gameid (keys $self->{games}->{$channel}->%*) {
|
foreach my $gameid (keys $self->{games}->{$channel}->%*) {
|
||||||
|
next if not exists $self->{games}->{$channel}->{$gameid}->{wordle};
|
||||||
if ($now - $self->{games}->{$channel}->{$gameid}->{guess_time} > 60 * 60 * 24) {
|
if ($now - $self->{games}->{$channel}->{$gameid}->{guess_time} > 60 * 60 * 24) {
|
||||||
my $wordle = join '', $self->{games}->{$channel}->{$gameid}->{wordle}->@*;
|
my $wordle = join '', $self->{games}->{$channel}->{$gameid}->{wordle}->@*;
|
||||||
|
|
||||||
|
|||||||
@ -25,8 +25,8 @@ use PBot::Imports;
|
|||||||
# These are set by the /misc/update_version script
|
# These are set by the /misc/update_version script
|
||||||
use constant {
|
use constant {
|
||||||
BUILD_NAME => "PBot",
|
BUILD_NAME => "PBot",
|
||||||
BUILD_REVISION => 4926,
|
BUILD_REVISION => 4929,
|
||||||
BUILD_DATE => "2025-11-09",
|
BUILD_DATE => "2025-12-19",
|
||||||
};
|
};
|
||||||
|
|
||||||
sub initialize {}
|
sub initialize {}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user