aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubos Kolouch <lubos@kolouch.net>2025-09-16 12:58:49 +0200
committerLubos Kolouch <lubos@kolouch.net>2025-09-16 12:58:49 +0200
commit15102c0f2062e2b1ac13519d75d4a9b227e704fc (patch)
tree1b7ec0d5d68f089d2e92283bc34d6720ec6acb23
parenta445a4de5501bcd731883b25ad138bace3417a8b (diff)
downloadperlweeklychallenge-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/README78
-rw-r--r--challenge-335/lubos-kolouch/perl/ch-1.pl73
-rw-r--r--challenge-335/lubos-kolouch/perl/ch-2.pl84
-rw-r--r--challenge-335/lubos-kolouch/python/ch-1.py46
-rw-r--r--challenge-335/lubos-kolouch/python/ch-2.py61
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()