diff options
| author | Lubos Kolouch <lubos@kolouch.net> | 2025-09-16 12:58:49 +0200 |
|---|---|---|
| committer | Lubos Kolouch <lubos@kolouch.net> | 2025-09-16 12:58:49 +0200 |
| commit | 15102c0f2062e2b1ac13519d75d4a9b227e704fc (patch) | |
| tree | 1b7ec0d5d68f089d2e92283bc34d6720ec6acb23 | |
| parent | a445a4de5501bcd731883b25ad138bace3417a8b (diff) | |
| download | perlweeklychallenge-club-15102c0f2062e2b1ac13519d75d4a9b227e704fc.tar.gz perlweeklychallenge-club-15102c0f2062e2b1ac13519d75d4a9b227e704fc.tar.bz2 perlweeklychallenge-club-15102c0f2062e2b1ac13519d75d4a9b227e704fc.zip | |
Add challenge 335 solutions for Lubos
| -rw-r--r-- | challenge-335/lubos-kolouch/README | 78 | ||||
| -rw-r--r-- | challenge-335/lubos-kolouch/perl/ch-1.pl | 73 | ||||
| -rw-r--r-- | challenge-335/lubos-kolouch/perl/ch-2.pl | 84 | ||||
| -rw-r--r-- | challenge-335/lubos-kolouch/python/ch-1.py | 46 | ||||
| -rw-r--r-- | challenge-335/lubos-kolouch/python/ch-2.py | 61 |
5 files changed, 342 insertions, 0 deletions
diff --git a/challenge-335/lubos-kolouch/README b/challenge-335/lubos-kolouch/README index 921b2d9f4a..6f4e46f4e3 100644 --- a/challenge-335/lubos-kolouch/README +++ b/challenge-335/lubos-kolouch/README @@ -1 +1,79 @@ Solutions by Lubos Kolouch. + +# The Weekly Challenge - 335 + +## Task 1: Common Characters + +### Description + +Given an array of words, return every character that appears in each word, +including duplicates. For example, `("bella", "label", "roller")` should yield +`("e", "l", "l")`. + +### Solution + +#### Perl (ch-1.pl) + +Builds frequency tables for every word and keeps the minimum count for each +character across all words. The retained characters are emitted according to +that minimum count. Unit tests cover the five official examples. + +#### Python (ch-1.py) + +Uses `collections.Counter` intersection (`&`) to compute the shared character +counts, then expands the characters sorted alphabetically. Includes `unittest` +cases mirroring the provided inputs. + +### Examples + +- Example 1: `("bella", "label", "roller")` → `("e", "l", "l")` +- Example 2: `("cool", "lock", "cook")` → `("c", "o")` +- Example 3: `("hello", "world", "pole")` → `("l", "o")` +- Example 4: `("abc", "def", "ghi")` → `()` +- Example 5: `("aab", "aac", "aaa")` → `("a", "a")` + +## Task 2: Find Winner + +### Description + +Analyze a tic-tac-toe game described by a sequence of moves. Players alternate +starting with A. Report `A` or `B` if a winner is decided, `Draw` if all nine +moves finish without a winner, or `Pending` otherwise. + +### Solution + +#### Perl (ch-2.pl) + +Applies each move to a 3×3 board, checks rows, columns, and diagonals for a win, +and returns the appropriate outcome. Embeds `Test::More` subtests for all five +examples. + +#### Python (ch-2.py) + +Replicates the board logic using lists, verifies win conditions, and determines +the resulting state. The accompanying `unittest` suite exercises all official +scenarios. + +### Examples + +- Example 1: `[[0,0],[2,0],[1,1],[2,1],[2,2]]` → `A` +- Example 2: `[[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]` → `B` +- Example 3: `[[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]` → `Draw` +- Example 4: `[[0,0],[1,1]]` → `Pending` +- Example 5: `[[1,1],[0,0],[2,2],[0,1],[1,0],[0,2]]` → `B` + +## Running the Code + +### Perl + +```bash +perl ch-1.pl +perl ch-2.pl +``` + +### Python + +```bash +python3 ch-1.py +python3 ch-2.py +``` diff --git a/challenge-335/lubos-kolouch/perl/ch-1.pl b/challenge-335/lubos-kolouch/perl/ch-1.pl new file mode 100644 index 0000000000..1f6a9d17d6 --- /dev/null +++ b/challenge-335/lubos-kolouch/perl/ch-1.pl @@ -0,0 +1,73 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +=head1 NAME + +ch-1.pl - The Weekly Challenge 335 Task 1: Common Characters + +=head1 DESCRIPTION + +Given an array of lowercase words, return every character that appears in each +word, preserving multiplicity. The solution builds frequency maps for every +word and keeps the minimum occurrence count per character. The final list +repeats each character according to that minimum count. + +=head1 AUTHOR + +Lubos Kolouch + +=cut + +use List::Util qw(min); +use Test::More tests => 5; + +use constant WordList => 'ARRAY'; + +sub common_characters { + my (@words) = @_; + return () if !@words; + + my %common; + $common{$_}++ for split //, $words[0]; + + for my $word ( @words[ 1 .. $#words ] ) { + my %freq; + $freq{$_}++ for split //, $word; + for my $char ( keys %common ) { + $common{$char} = exists $freq{$char} ? min( $common{$char}, $freq{$char} ) : 0; + } + delete @common{ grep { $common{$_} == 0 } keys %common }; + } + + my @result; + for my $char ( sort keys %common ) { + push @result, ($char) x $common{$char}; + } + return @result; +} + +subtest 'Example 1' => sub { + my @words = qw(bella label roller); + is_deeply( [ common_characters(@words) ], [qw(e l l)], 'Common characters e, l, l' ); +}; + +subtest 'Example 2' => sub { + my @words = qw(cool lock cook); + is_deeply( [ common_characters(@words) ], [qw(c o)], 'Common characters c, o' ); +}; + +subtest 'Example 3' => sub { + my @words = qw(hello world pole); + is_deeply( [ common_characters(@words) ], [qw(l o)], 'Common characters l, o' ); +}; + +subtest 'Example 4' => sub { + my @words = qw(abc def ghi); + is_deeply( [ common_characters(@words) ], [], 'No common characters' ); +}; + +subtest 'Example 5' => sub { + my @words = qw(aab aac aaa); + is_deeply( [ common_characters(@words) ], [qw(a a)], 'Two common a characters' ); +}; diff --git a/challenge-335/lubos-kolouch/perl/ch-2.pl b/challenge-335/lubos-kolouch/perl/ch-2.pl new file mode 100644 index 0000000000..0328a12887 --- /dev/null +++ b/challenge-335/lubos-kolouch/perl/ch-2.pl @@ -0,0 +1,84 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +=head1 NAME + +ch-2.pl - The Weekly Challenge 335 Task 2: Find Winner + +=head1 DESCRIPTION + +Given a sequence of tic-tac-toe moves played on a 3x3 grid (starting with +player A and alternating), determine the game outcome. A win occurs when either +player claims an entire row, column, or diagonal. If both players complete all +moves without a winner, the result is a Draw; otherwise, the game is Pending. + +=head1 AUTHOR + +Lubos Kolouch + +=cut + +use List::Util qw(any); +use Test::More tests => 5; + +use constant MoveList => 'ARRAY'; + +sub find_winner { + my (@moves) = @_; + + my @board = map { [ ('_') x 3 ] } 1 .. 3; + my $player = 'A'; + for my $move (@moves) { + my ( $row, $col ) = @$move; + $board[$row][$col] = $player; + $player = $player eq 'A' ? 'B' : 'A'; + } + + my $winner = _check_winner( \@board ); + return $winner if $winner; + + return @moves == 9 ? 'Draw' : 'Pending'; +} + +sub _check_winner { + my ($board) = @_; + + my @lines; + push @lines, $board->[$_] for 0 .. 2; + push @lines, [ map { $board->[$_][0] } 0 .. 2 ]; + push @lines, [ map { $board->[$_][1] } 0 .. 2 ]; + push @lines, [ map { $board->[$_][2] } 0 .. 2 ]; + push @lines, [ map { $board->[$_][$_] } 0 .. 2 ]; + push @lines, [ map { $board->[$_][ 2 - $_ ] } 0 .. 2 ]; + + for my $line (@lines) { + return $line->[0] if $line->[0] ne '_' && $line->[0] eq $line->[1] && $line->[1] eq $line->[2]; + } + return ''; +} + +subtest 'Example 1' => sub { + my @moves = ( [ 0, 0 ], [ 2, 0 ], [ 1, 1 ], [ 2, 1 ], [ 2, 2 ] ); + is( find_winner(@moves), 'A', 'Player A wins' ); +}; + +subtest 'Example 2' => sub { + my @moves = ( [ 0, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0, 2 ], [ 1, 0 ], [ 2, 0 ] ); + is( find_winner(@moves), 'B', 'Player B wins' ); +}; + +subtest 'Example 3' => sub { + my @moves = ( [ 0, 0 ], [ 1, 1 ], [ 2, 0 ], [ 1, 0 ], [ 1, 2 ], [ 2, 1 ], [ 0, 1 ], [ 0, 2 ], [ 2, 2 ] ); + is( find_winner(@moves), 'Draw', 'Game ends in draw' ); +}; + +subtest 'Example 4' => sub { + my @moves = ( [ 0, 0 ], [ 1, 1 ] ); + is( find_winner(@moves), 'Pending', 'Game still pending' ); +}; + +subtest 'Example 5' => sub { + my @moves = ( [ 1, 1 ], [ 0, 0 ], [ 2, 2 ], [ 0, 1 ], [ 1, 0 ], [ 0, 2 ] ); + is( find_winner(@moves), 'B', 'Player B wins with top row' ); +}; diff --git a/challenge-335/lubos-kolouch/python/ch-1.py b/challenge-335/lubos-kolouch/python/ch-1.py new file mode 100644 index 0000000000..bd97c831c8 --- /dev/null +++ b/challenge-335/lubos-kolouch/python/ch-1.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""The Weekly Challenge 335 Task 1: Common Characters.""" +from collections import Counter +import unittest + +WordList = list[str] + + +def common_characters(words: WordList) -> list[str]: + """Return characters that appear in every word, keeping duplicate counts.""" + if not words: + return [] + + common = Counter(words[0]) + for word in words[1:]: + common &= Counter(word) + result: list[str] = [] + for char in sorted(common.keys()): + result.extend([char] * common[char]) + return result + + +class TestCommonCharacters(unittest.TestCase): + """Unit tests for the provided challenge examples.""" + + def test_example1(self) -> None: + self.assertEqual(common_characters(["bella", "label", "roller"]), + ["e", "l", "l"]) + + def test_example2(self) -> None: + self.assertEqual(common_characters(["cool", "lock", "cook"]), + ["c", "o"]) + + def test_example3(self) -> None: + self.assertEqual(common_characters(["hello", "world", "pole"]), + ["l", "o"]) + + def test_example4(self) -> None: + self.assertEqual(common_characters(["abc", "def", "ghi"]), []) + + def test_example5(self) -> None: + self.assertEqual(common_characters(["aab", "aac", "aaa"]), ["a", "a"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/challenge-335/lubos-kolouch/python/ch-2.py b/challenge-335/lubos-kolouch/python/ch-2.py new file mode 100644 index 0000000000..ee4948c871 --- /dev/null +++ b/challenge-335/lubos-kolouch/python/ch-2.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""The Weekly Challenge 335 Task 2: Find Winner.""" +import unittest + +MoveList = list[list[int]] + + +def find_winner(moves: MoveList) -> str: + """Return the tic-tac-toe game outcome for the given move sequence.""" + board = [["_" for _ in range(3)] for _ in range(3)] + players = ["A", "B"] + + for index, (row, col) in enumerate(moves): + board[row][col] = players[index % 2] + + winner = _check_winner(board) + if winner: + return winner + return "Draw" if len(moves) == 9 else "Pending" + + +def _check_winner(board: list[list[str]]) -> str: + lines = [] + lines.extend(board) + lines.extend([[board[r][c] for r in range(3)] for c in range(3)]) + lines.append([board[i][i] for i in range(3)]) + lines.append([board[i][2 - i] for i in range(3)]) + + for line in lines: + if line[0] != "_" and line.count(line[0]) == 3: + return line[0] + return "" + + +class TestFindWinner(unittest.TestCase): + """Unit tests derived from the challenge examples.""" + + def test_example1(self) -> None: + moves = [[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]] + self.assertEqual(find_winner(moves), "A") + + def test_example2(self) -> None: + moves = [[0, 0], [1, 1], [0, 1], [0, 2], [1, 0], [2, 0]] + self.assertEqual(find_winner(moves), "B") + + def test_example3(self) -> None: + moves = [[0, 0], [1, 1], [2, 0], [1, 0], [1, 2], [2, 1], [0, 1], + [0, 2], [2, 2]] + self.assertEqual(find_winner(moves), "Draw") + + def test_example4(self) -> None: + moves = [[0, 0], [1, 1]] + self.assertEqual(find_winner(moves), "Pending") + + def test_example5(self) -> None: + moves = [[1, 1], [0, 0], [2, 2], [0, 1], [1, 0], [0, 2]] + self.assertEqual(find_winner(moves), "B") + + +if __name__ == "__main__": + unittest.main() |
