aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad Sajid Anwar <Mohammad.Anwar@yahoo.com>2025-09-16 22:45:47 +0100
committerGitHub <noreply@github.com>2025-09-16 22:45:47 +0100
commit543492bf9fd8fec289fb1ae2c64d2b5f6e65831d (patch)
tree700f5e6fcff5e753e13bb18d08a479281b86b488
parent6db49c7b6f8614e89d12611ddbd80dc5e8caf454 (diff)
parent15102c0f2062e2b1ac13519d75d4a9b227e704fc (diff)
downloadperlweeklychallenge-club-543492bf9fd8fec289fb1ae2c64d2b5f6e65831d.tar.gz
perlweeklychallenge-club-543492bf9fd8fec289fb1ae2c64d2b5f6e65831d.tar.bz2
perlweeklychallenge-club-543492bf9fd8fec289fb1ae2c64d2b5f6e65831d.zip
Merge pull request #12688 from LubosKolouch/master
Challenges 335 and 336 solutions for Lubos Kolouch
-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
-rw-r--r--challenge-336/lubos-kolouch/README76
-rw-r--r--challenge-336/lubos-kolouch/perl/ch-1.pl80
-rw-r--r--challenge-336/lubos-kolouch/perl/ch-2.pl83
-rw-r--r--challenge-336/lubos-kolouch/python/ch-1.py45
-rw-r--r--challenge-336/lubos-kolouch/python/ch-2.py48
10 files changed, 674 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()
diff --git a/challenge-336/lubos-kolouch/README b/challenge-336/lubos-kolouch/README
index 921b2d9f4a..a8bfeee6c2 100644
--- a/challenge-336/lubos-kolouch/README
+++ b/challenge-336/lubos-kolouch/README
@@ -1 +1,77 @@
Solutions by Lubos Kolouch.
+
+# The Weekly Challenge - 336
+
+## Task 1: Equal Group
+
+### Description
+
+Given an array of integers, determine whether it can be partitioned into one or
+more groups of equal size (at least two elements per group) such that every
+member within a group has the same value.
+
+### Solution
+
+#### Perl (ch-1.pl)
+
+Counts occurrences of each value, ensures every frequency is at least two, and
+computes the greatest common divisor across the frequencies. A GCD of two or
+more confirms that the array can be divided into uniform groups. Embedded
+`Test::More` cases cover the official examples.
+
+#### Python (ch-1.py)
+
+Uses `collections.Counter` and `math.gcd` to mirror the Perl logic. Type hints
+and `unittest` tests verify the five examples supplied in the challenge.
+
+### Examples
+
+- Example 1: `[1, 1, 2, 2, 2, 2]` → `True`
+- Example 2: `[1, 1, 1, 2, 2, 2, 3, 3]` → `False`
+- Example 3: `[5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7]` → `True`
+- Example 4: `[1, 2, 3, 4]` → `False`
+- Example 5: `[8, 8, 9, 9, 10, 10, 11, 11]` → `True`
+
+## Task 2: Final Score
+
+### Description
+
+Given a sequence of scoring operations consisting of integers, `C`, `D`, and
+`+`, compute the final total. `C` removes the previous score, `D` doubles it,
+and `+` adds the last two scores.
+
+### Solution
+
+#### Perl (ch-2.pl)
+
+Maintains a stack of recorded scores, applying each operation as specified, and
+returns the sum of the stack. `Test::More` subtests cover the challenge cases.
+
+#### Python (ch-2.py)
+
+Implements the same stack-based logic using type annotations and `unittest`
+coverage for the provided examples.
+
+### Examples
+
+- Example 1: `["5", "2", "C", "D", "+"]` → `30`
+- Example 2: `["5", "-2", "4", "C", "D", "9", "+", "+"]` → `27`
+- Example 3: `["7", "D", "D", "C", "+", "3"]` → `45`
+- Example 4: `["-5", "-10", "+", "D", "C", "+"]` → `-55`
+- Example 5: `["3", "6", "+", "D", "C", "8", "+", "D", "-2", "C", "+"]` → `128`
+
+## 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-336/lubos-kolouch/perl/ch-1.pl b/challenge-336/lubos-kolouch/perl/ch-1.pl
new file mode 100644
index 0000000000..3a4a7d6f10
--- /dev/null
+++ b/challenge-336/lubos-kolouch/perl/ch-1.pl
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+=head1 NAME
+
+ch-1.pl - The Weekly Challenge 336 Task 1: Equal Group
+
+=head1 DESCRIPTION
+
+Determine whether an array of integers can be partitioned into one or more
+groups so that:
+
+=over 4
+
+=item * all groups have the same size (at least two members), and
+
+=item * all values inside each group are identical.
+
+=back
+
+The implementation counts occurrences of each value and verifies that the
+greatest common divisor of the counts is at least two.
+
+=head1 AUTHOR
+
+Lubos Kolouch
+
+=cut
+
+use List::Util qw(all reduce);
+use Test::More tests => 5;
+
+use constant IntArray => 'ARRAY';
+
+sub _gcd {
+ my ( $a, $b ) = @_;
+ ( $a, $b ) = ( abs $a, abs $b );
+ return $a if $b == 0;
+ return _gcd( $b, $a % $b );
+}
+
+sub equal_group {
+ my (@ints) = @_;
+ return 0 if @ints < 2;
+
+ my %freq;
+ $freq{$_}++ for @ints;
+ my @counts = values %freq;
+
+ return 0 if !all { $_ >= 2 } @counts;
+
+ my $gcd = reduce { _gcd( $a, $b ) } @counts;
+ return $gcd >= 2 ? 1 : 0;
+}
+
+subtest 'Example 1' => sub {
+ my @ints = ( 1, 1, 2, 2, 2, 2 );
+ ok( equal_group(@ints), 'Groups (1,1), (2,2), (2,2)' );
+};
+
+subtest 'Example 2' => sub {
+ my @ints = ( 1, 1, 1, 2, 2, 2, 3, 3 );
+ ok( !equal_group(@ints), 'Cannot group equally' );
+};
+
+subtest 'Example 3' => sub {
+ my @ints = ( 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7 );
+ ok( equal_group(@ints), 'Groups of size 6' );
+};
+
+subtest 'Example 4' => sub {
+ my @ints = ( 1, 2, 3, 4 );
+ ok( !equal_group(@ints), 'Distinct values cannot group' );
+};
+
+subtest 'Example 5' => sub {
+ my @ints = ( 8, 8, 9, 9, 10, 10, 11, 11 );
+ ok( equal_group(@ints), 'Pairs of equal values' );
+};
diff --git a/challenge-336/lubos-kolouch/perl/ch-2.pl b/challenge-336/lubos-kolouch/perl/ch-2.pl
new file mode 100644
index 0000000000..8aceb3b007
--- /dev/null
+++ b/challenge-336/lubos-kolouch/perl/ch-2.pl
@@ -0,0 +1,83 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+=head1 NAME
+
+ch-2.pl - The Weekly Challenge 336 Task 2: Final Score
+
+=head1 DESCRIPTION
+
+Compute the total score for a sequence of operations. Each entry is one of:
+
+=over 4
+
+=item * an integer representing a new score,
+
+=item * C<'C'> to invalidate and remove the previous score,
+
+=item * C<'D'> to record double the previous score, or
+
+=item * C<'+'> to add the sum of the previous two scores.
+
+=back
+
+The solution tracks scores on a stack and sums the remaining values.
+
+=head1 AUTHOR
+
+Lubos Kolouch
+
+=cut
+
+use List::Util qw(sum0);
+use Test::More tests => 5;
+
+use constant ScoreList => 'ARRAY';
+
+sub final_score {
+ my (@entries) = @_;
+ my @record;
+
+ for my $entry (@entries) {
+ if ( $entry eq 'C' ) {
+ pop @record;
+ }
+ elsif ( $entry eq 'D' ) {
+ push @record, 2 * $record[-1];
+ }
+ elsif ( $entry eq '+' ) {
+ push @record, $record[-1] + $record[-2];
+ }
+ else {
+ push @record, 0 + $entry;
+ }
+ }
+
+ return sum0(@record);
+}
+
+subtest 'Example 1' => sub {
+ my @scores = qw(5 2 C D +);
+ is( final_score(@scores), 30, 'Total score 30' );
+};
+
+subtest 'Example 2' => sub {
+ my @scores = qw(5 -2 4 C D 9 + +);
+ is( final_score(@scores), 27, 'Total score 27' );
+};
+
+subtest 'Example 3' => sub {
+ my @scores = qw(7 D D C + 3);
+ is( final_score(@scores), 45, 'Total score 45' );
+};
+
+subtest 'Example 4' => sub {
+ my @scores = qw(-5 -10 + D C +);
+ is( final_score(@scores), -55, 'Total score -55' );
+};
+
+subtest 'Example 5' => sub {
+ my @scores = qw(3 6 + D C 8 + D -2 C +);
+ is( final_score(@scores), 128, 'Total score 128' );
+};
diff --git a/challenge-336/lubos-kolouch/python/ch-1.py b/challenge-336/lubos-kolouch/python/ch-1.py
new file mode 100644
index 0000000000..2643543a6d
--- /dev/null
+++ b/challenge-336/lubos-kolouch/python/ch-1.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+"""The Weekly Challenge 336 Task 1: Equal Group."""
+from collections import Counter
+from math import gcd
+import unittest
+
+IntList = list[int]
+
+
+def equal_group(ints: IntList) -> bool:
+ """Return True if values can be partitioned into equal homogeneous groups."""
+ if len(ints) < 2:
+ return False
+
+ counts = Counter(ints)
+ if any(count < 2 for count in counts.values()):
+ return False
+
+ group_size = 0
+ for count in counts.values():
+ group_size = gcd(group_size, count)
+ return group_size >= 2
+
+
+class TestEqualGroup(unittest.TestCase):
+ """Unit tests using the provided examples."""
+
+ def test_example1(self) -> None:
+ self.assertTrue(equal_group([1, 1, 2, 2, 2, 2]))
+
+ def test_example2(self) -> None:
+ self.assertFalse(equal_group([1, 1, 1, 2, 2, 2, 3, 3]))
+
+ def test_example3(self) -> None:
+ self.assertTrue(equal_group([5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7]))
+
+ def test_example4(self) -> None:
+ self.assertFalse(equal_group([1, 2, 3, 4]))
+
+ def test_example5(self) -> None:
+ self.assertTrue(equal_group([8, 8, 9, 9, 10, 10, 11, 11]))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/challenge-336/lubos-kolouch/python/ch-2.py b/challenge-336/lubos-kolouch/python/ch-2.py
new file mode 100644
index 0000000000..a7dc021f95
--- /dev/null
+++ b/challenge-336/lubos-kolouch/python/ch-2.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+"""The Weekly Challenge 336 Task 2: Final Score."""
+import unittest
+
+ScoreList = list[str]
+
+
+def final_score(scores: ScoreList) -> int:
+ """Calculate the total score from a list of operations."""
+ record: list[int] = []
+ for entry in scores:
+ if entry == "C":
+ record.pop()
+ elif entry == "D":
+ record.append(2 * record[-1])
+ elif entry == "+":
+ record.append(record[-1] + record[-2])
+ else:
+ record.append(int(entry))
+ return sum(record)
+
+
+class TestFinalScore(unittest.TestCase):
+ """Unit tests derived from the specification examples."""
+
+ def test_example1(self) -> None:
+ self.assertEqual(final_score(["5", "2", "C", "D", "+"]), 30)
+
+ def test_example2(self) -> None:
+ self.assertEqual(
+ final_score(["5", "-2", "4", "C", "D", "9", "+", "+"]), 27)
+
+ def test_example3(self) -> None:
+ self.assertEqual(final_score(["7", "D", "D", "C", "+", "3"]), 45)
+
+ def test_example4(self) -> None:
+ self.assertEqual(final_score(["-5", "-10", "+", "D", "C", "+"]), -55)
+
+ def test_example5(self) -> None:
+ self.assertEqual(
+ final_score(
+ ["3", "6", "+", "D", "C", "8", "+", "D", "-2", "C", "+"]),
+ 128,
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()