aboutsummaryrefslogtreecommitdiff
path: root/challenge-145
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2022-01-03 02:04:23 +0000
committerGitHub <noreply@github.com>2022-01-03 02:04:23 +0000
commit36fa420d62b3ef0a172b2ab08c6ccd75290babf6 (patch)
treef31afed9a1e10bffcaf45e396fc99b58408b187a /challenge-145
parent8aa7995f9f7ab7f2eded5765ff486cd976b8afa5 (diff)
parent743b6f1f2a5ae0edb0d3c31a41eb9ef1787b20c0 (diff)
downloadperlweeklychallenge-club-36fa420d62b3ef0a172b2ab08c6ccd75290babf6.tar.gz
perlweeklychallenge-club-36fa420d62b3ef0a172b2ab08c6ccd75290babf6.tar.bz2
perlweeklychallenge-club-36fa420d62b3ef0a172b2ab08c6ccd75290babf6.zip
Merge pull request #5455 from iangoodnight/iangoodnight_145
week 145
Diffstat (limited to 'challenge-145')
-rwxr-xr-xchallenge-145/iangoodnight/javascript/ch-1.js165
-rwxr-xr-xchallenge-145/iangoodnight/javascript/ch-2.js182
-rwxr-xr-xchallenge-145/iangoodnight/perl/ch-1.pl162
-rwxr-xr-xchallenge-145/iangoodnight/perl/ch-2.pl200
-rwxr-xr-xchallenge-145/iangoodnight/python/ch-1.py100
-rwxr-xr-xchallenge-145/iangoodnight/python/ch-2.py142
-rwxr-xr-xchallenge-145/iangoodnight/ruby/ch-1.rb81
-rwxr-xr-xchallenge-145/iangoodnight/ruby/ch-2.rb125
8 files changed, 1157 insertions, 0 deletions
diff --git a/challenge-145/iangoodnight/javascript/ch-1.js b/challenge-145/iangoodnight/javascript/ch-1.js
new file mode 100755
index 0000000000..380df8f7f8
--- /dev/null
+++ b/challenge-145/iangoodnight/javascript/ch-1.js
@@ -0,0 +1,165 @@
+#!/usr/bin/env node
+// ch-1.js
+
+/*******************************************************************************
+ * https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+ *
+ * ## Task 1 > Dot Product
+ * =======================
+ *
+ * You are given 2 arrays of the same size, `@a` and `@b`.
+ *
+ * Write a script to implement `Dot Product`.
+ *
+ * **Example:**
+ * ```
+ * @a = (1, 2, 3);
+ * @b = (4, 5, 6);
+ *
+ * $dot_product = (1 * 4) + (2 * 5) + (3 * 6) => 4 + 10 + 18 => 32
+ * ```
+ *
+ ******************************************************************************/
+
+'use strict';
+
+/*******************************************************************************
+ * Dependencies ****************************************************************
+ ******************************************************************************/
+
+const fs = require('fs');
+
+const path = require('path');
+
+/*******************************************************************************
+ * PWC Solution ****************************************************************
+ ******************************************************************************/
+
+// Input guard
+function areSameLengthArrays(arr1 = [], arr2 = []) {
+ const { isArray } = Array;
+
+ return isArray(arr1) && isArray(arr2) && arr1.length === arr2.length;
+}
+
+// Solution
+function dotProduct(arr1 = [], arr2 = []) {
+ const badInput = 'dotProduct expects two equal-length arrays as arguments';
+
+ if (!areSameLengthArrays(arr1, arr2)) throw new Error(badInput);
+
+ return arr1.reduce((sum, multiplicand, idx) => {
+ const multiplier = arr2[idx];
+
+ return sum + multiplicand * multiplier;
+ }, 0);
+}
+
+/*******************************************************************************
+ * Utilities *******************************************************************
+ ******************************************************************************/
+
+// Checking file types
+function isFile(filePath) {
+ return fs.lstatSync(filePath).isFile();
+}
+
+function isDirectory(filePath) {
+ return fs.lstatSync(filePath).isDirectory();
+}
+
+// String transformation
+function numArrayFromString(str = '') {
+ return str.split(',').map((numStr) => parseInt(numStr.trim(), 10));
+}
+
+// File handlers
+function parseTestCase(filePath = '') {
+ if (isFile(filePath)) {
+ try {
+ const data = fs.readFileSync(filePath, 'utf8');
+
+ const lines = data.split('\n');
+
+ const badCase = 'Test cases are improperly formatted';
+
+ if (!lines.length) throw new Error(badCase);
+
+ const testData = lines.filter(
+ (line) => line.trim().length !== 0 && line.trim().charAt(0) !== '#',
+ );
+
+ const [firstLine, secondLine, testLine] = testData;
+
+ const arr1 = numArrayFromString(firstLine);
+
+ const arr2 = numArrayFromString(secondLine);
+
+ if (!areSameLengthArrays(arr1, arr2)) throw new Error(badCase);
+
+ const test = parseInt(testLine.trim(), 10);
+
+ if (Number.isNaN(test)) throw new Error(badCase);
+
+ return [arr1, arr2, test];
+ } catch (error) {
+ return console.log('Problems parsing test cases: ', error);
+ }
+ }
+ console.log(filePath, 'is not a file');
+
+ return null;
+}
+
+function printArray(arr = []) {
+ return Array.isArray(arr) && arr.join(', ');
+}
+
+function assertDotProduct([arr1 = [], arr2 = [], test], filePath = '') {
+ try {
+ const result = dotProduct(arr1, arr2);
+
+ const testPath = filePath !== '' ? `${filePath}: ` : '';
+
+ console.log(testPath);
+ console.log(`@a = ${printArray(arr1)}`);
+ console.log(`@b = ${printArray(arr2)}`);
+ console.log(`Dot Product: ${result}`);
+
+ if (result === test) {
+ return console.log('\x1b[32m%s\x1b[0m', 'Passed \u2690\n');
+ }
+
+ return console.log('\x1b[31m%s\x1b[0m', 'Failed \u2715\n');
+ } catch (error) {
+ return console.log('Something went wrong: ', error);
+ }
+}
+
+/*******************************************************************************
+ * Test runner *****************************************************************
+ ******************************************************************************/
+
+(function main() {
+ const testPath = process.argv[2] || '../test_cases/ch-1';
+
+ try {
+ if (isFile(testPath)) {
+ const testElements = parseTestCase(testPath);
+
+ return assertDotProduct(testElements, testPath);
+ }
+ if (isDirectory(testPath)) {
+ return fs.readdirSync(testPath).forEach((fileName) => {
+ const filePath = path.join(testPath, fileName);
+
+ const testElements = parseTestCase(filePath);
+
+ return testElements && assertDotProduct(testElements, filePath);
+ });
+ }
+ return console.log('No tests found');
+ } catch (error) {
+ return console.log('Something went wrong: ', error);
+ }
+})();
diff --git a/challenge-145/iangoodnight/javascript/ch-2.js b/challenge-145/iangoodnight/javascript/ch-2.js
new file mode 100755
index 0000000000..f0a8686fa9
--- /dev/null
+++ b/challenge-145/iangoodnight/javascript/ch-2.js
@@ -0,0 +1,182 @@
+#!/usr/bin/env node
+// ch-1.js
+
+/*******************************************************************************
+ * https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+ *
+ * ## Task2 > Palindromic Tree
+ * ===========================
+ *
+ * You are given a string `$s`.
+ *
+ * Write a script to create a `Palindromic Tree` for the given string
+ *
+ * I found this [blog] explaining `Palindromic Tree` in detail.
+ *
+ * **Example 1:**
+ *
+ * ```
+ * Input: $s = 'redivider'
+ * Output: r redivider e edivide d divid i ivi v
+ * ```
+ *
+ * **Example 2:**
+ *
+ * ```
+ * Input: $s = 'deific'
+ * Output: d e i ifi f c
+ * ```
+ *
+ * **Example 3:**
+ *
+ * ```
+ * Input: $s = 'rotors'
+ * Output: r rotor o oto t s
+ * ```
+ *
+ * **Example 4:**
+ *
+ * ```
+ * Input: $s = 'challenge'
+ * Output: c h a l ll e n g
+ * ```
+ *
+ * **Example 5:**
+ *
+ * ```
+ * Input: $s = 'champion'
+ * Output: c h a m p i o n
+ * ```
+ *
+ * **Example 6**
+ *
+ * ```
+ * Input: $s = 'christmas'
+ * Output: c h r i s t m a
+ * ```
+ *
+ * [blog]: https://medium.com/@alessiopiergiacomi/eertree-or-palindromic-tree-82453e75025b
+ *
+ ******************************************************************************/
+
+'use strict';
+
+const fs = require('fs');
+
+const path = require('path');
+
+/*******************************************************************************
+ * PWC Solution ****************************************************************
+ ******************************************************************************/
+
+function getPalindromes(str = '') {
+ const isPalindrome = (s = '') => s === s.split('').reverse().join('');
+
+ const palindromes = [];
+
+ [...[...str].keys()].forEach((idx) => {
+ const substr = str.slice(0, idx + 1);
+
+ if (isPalindrome(substr)) palindromes.push(substr);
+ });
+
+ return palindromes;
+}
+
+// The blog mentioned in the challenge description describes a pretty
+// interesting data structure to tackle this challenge. My approach here is not
+// nearly as nice.
+function eertree(str = '') {
+ let palindromes = [];
+
+ [...[...str].keys()].forEach((idx) => {
+ const substr = str.slice(idx);
+
+ const subPalindromes = getPalindromes(substr);
+
+ palindromes = Array.from(new Set([...palindromes, ...subPalindromes]));
+ });
+
+ return palindromes.join(' ');
+}
+
+/*******************************************************************************
+ * Utilities *******************************************************************
+ ******************************************************************************/
+
+function parseTestCase(filePath = '') {
+ try {
+ const data = fs.readFileSync(filePath, 'utf8');
+
+ const lines = data.split('\n');
+
+ const [input, output] = lines.filter(
+ (line) => line.trim().charAt(0) !== '#',
+ );
+
+ return [input.trim(), output.trim()];
+ } catch (error) {
+ console.log(`Problems parsing ${filePath}: ${error}`);
+ return [];
+ }
+}
+
+function printParams(filePath = '', input = '', output = '') {
+ console.log(`${filePath}:\nInput: $s = ${input}\nOutput: ${output}`);
+ return true;
+}
+
+function testSolution(solution = () => {}, input = '', test = '') {
+ const result = solution(input);
+
+ if (test === result) {
+ return console.log('\x1b[32m%s\x1b[0m', 'Passed \u2690\n');
+ }
+ return console.log('\x1b[31m%s\x1b[0m', 'Failed \u2715\n');
+}
+
+function isFile(filePath) {
+ return fs.lstatSync(filePath).isFile();
+}
+
+function isDirectory(filePath) {
+ return fs.lstatSync(filePath).isDirectory();
+}
+
+/*******************************************************************************
+ * Main ************************************************************************
+ ******************************************************************************/
+
+(function main() {
+ const testPath = process.argv[2] || '../test_cases/ch-2';
+
+ try {
+ if (isFile(testPath)) {
+ const [input, test] = parseTestCase(testPath);
+
+ return (
+ input &&
+ test &&
+ printParams(testPath, input, test) &&
+ testSolution(eertree, input, test)
+ );
+ }
+ if (isDirectory(testPath)) {
+ return fs.readdirSync(testPath).forEach((fileName) => {
+ const filePath = path.join(testPath, fileName);
+
+ const [input, test] = parseTestCase(filePath);
+
+ return (
+ input &&
+ test &&
+ printParams(filePath, input, test) &&
+ testSolution(eertree, input, test)
+ );
+ });
+ }
+ return 1;
+ } catch (error) {
+ return console.log('Something went wrong:', error);
+ }
+})();
diff --git a/challenge-145/iangoodnight/perl/ch-1.pl b/challenge-145/iangoodnight/perl/ch-1.pl
new file mode 100755
index 0000000000..62c6d55322
--- /dev/null
+++ b/challenge-145/iangoodnight/perl/ch-1.pl
@@ -0,0 +1,162 @@
+#!/usr/bin/perl
+# ch-1.pl
+
+=begin comment
+
+ > https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+
+ ## Task 1 > Dot Product
+ =======================
+
+ You are given 2 arrays of the same size, `@a` and `@b`.
+
+ Write a script to implement `Dot Product`.
+
+ **Example:**
+ ```
+ @a = (1, 2, 3);
+ @b = (4, 5, 6);
+
+ $dot_product = (1 * 4) + (2 * 5) + (3 * 6) => 4 + 10 + 18 => 32
+ ```
+
+=end comment
+=cut
+
+use strict;
+use warnings;
+use utf8;
+use open ':std', ':encoding(UTF-8)';
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+sub dot_product {
+
+ my @arr_1 = @{ +shift };
+ my @arr_2 = @{ +shift };
+
+ my $sum = 0;
+
+ for my $idx ( 0 .. $#arr_1 ) {
+ $sum += $arr_1[$idx] * $arr_2[$idx];
+ }
+ return $sum;
+}
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+sub nums_from_string {
+ my $string = shift;
+
+ return [ map { $_ * 1 } split / \s* , \s* /x, $string ];
+}
+
+sub parse_test_case {
+ my $filename = shift;
+ my $arr_1;
+ my $arr_2;
+ my $test;
+
+ open my $fh, '<', $filename
+ or die "Could not open '$filename' - $!\n";
+
+ while ( my $line = <$fh> ) {
+ chomp $line;
+
+ next if $line =~ m/ \A \s* \# /x;
+
+ $line =~ s/ \A \s+ | \s+ \z //gx;
+
+ if ( !defined $arr_1 ) {
+ $arr_1 = nums_from_string($line);
+ next;
+ }
+
+ if ( !defined $arr_2 ) {
+ $arr_2 = nums_from_string($line);
+ next;
+ }
+
+ $test = $line * 1;
+ }
+
+ close $fh;
+
+ return ( $arr_1, $arr_2, $test );
+}
+
+sub test_solution {
+ my $callback = shift;
+ my @args = @{ +shift };
+ my $test = shift;
+
+ my $result = &$callback(@args);
+
+ if ( $test == $result ) {
+ print "\e[32mPassed \x{2690}\e[0m\n";
+ return 1;
+ }
+ print "\e[31mFailed \x{2715}\e[0m\n";
+ return 0;
+}
+
+sub print_arguments {
+ my ( $target, $a, $b, $test ) = @_;
+
+ print $target, ":\n";
+ print '@a = ', join( ', ', @$a ), "\n";
+ print '@b = ', join( ', ', @$b ), "\n";
+ print "Dot Product: $test\n";
+
+ return 1;
+}
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+sub main {
+ my $target = shift @ARGV // '../test_cases/ch-1';
+
+ if ( -e -r -f $target ) {
+ my ( $arr_1, $arr_2, $test ) = parse_test_case $target;
+
+ print_arguments( $target, $arr_1, $arr_2, $test );
+ test_solution( \&dot_product, [ $arr_1, $arr_2 ], $test );
+ print "\n";
+ }
+ elsif ( -e -r -d _ ) {
+ $target =~ s/ \A ( .*? ) \/? \z /$1\//x;
+
+ opendir my $dh, $target
+ or die "Could not open '$target' - $!\n";
+
+ my @entries = readdir $dh;
+
+ closedir $dh;
+
+ foreach my $entry ( sort @entries ) {
+ if ( $entry eq q{.} or $entry eq q{..} ) {
+ next;
+ }
+
+ my $path = $target . $entry;
+ my ( $arr_1, $arr_2, $test ) = parse_test_case $path;
+
+ print_arguments( $path, $arr_1, $arr_2, $test );
+ test_solution( \&dot_product, [ $arr_1, $arr_2 ], $test );
+ print "\n";
+ }
+ }
+ else {
+ print "No test files found at $target\n";
+ }
+
+ return 0;
+}
+
+main();
diff --git a/challenge-145/iangoodnight/perl/ch-2.pl b/challenge-145/iangoodnight/perl/ch-2.pl
new file mode 100755
index 0000000000..953ef12e6e
--- /dev/null
+++ b/challenge-145/iangoodnight/perl/ch-2.pl
@@ -0,0 +1,200 @@
+#!/usr/bin/perl
+# ch-1.pl
+
+=begin comment
+
+ > https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+
+ ## Task2 > Palindromic Tree
+ ===========================
+
+ You are given a string `$s`.
+
+ Write a script to create a `Palindromic Tree` for the given string
+
+ I found this [blog] explaining `Palindromic Tree` in detail.
+
+ **Example 1:**
+
+ ```
+ Input: $s = 'redivider'
+ Output: r redivider e edivide d divid i ivi v
+ ```
+
+ **Example 2:**
+
+ ```
+ Input: $s = 'deific'
+ Output: d e i ifi f c
+ ```
+
+ **Example 3:**
+
+ ```
+ Input: $s = 'rotors'
+ Output: r rotor o oto t s
+ ```
+
+ **Example 4:**
+
+ ```
+ Input: $s = 'challenge'
+ Output: c h a l ll e n g
+ ```
+
+ **Example 5:**
+
+ ```
+ Input: $s = 'champion'
+ Output: c h a m p i o n
+ ```
+
+ **Example 6**
+
+ ```
+ Input: $s = 'christmas'
+ Output: c h r i s t m a
+ ```
+
+ [blog]: https://medium.com/@alessiopiergiacomi/eertree-or-palindromic-tree-82453e75025b
+
+=end comment
+=cut
+
+use strict;
+use warnings;
+use utf8;
+use open ':std', ':encoding(UTF-8)';
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+sub is_palindrome {
+ my $string = shift;
+ my $reversed = reverse $string;
+
+ return $string eq $reversed ? 1 : 0;
+}
+
+# The blog mentioned in the challenge description describes a pretty interesting
+# data structure to tackle this challenge. My approach here is not nearly as
+# nice.
+sub eertree {
+ my $input_str = shift;
+ my $length = length $input_str;
+ my @palindromes;
+ my %map;
+
+ for my $cursor ( 0 .. $length - 1 ) {
+ my $chars = 1;
+
+ while ( $chars <= $length - $cursor ) {
+ my $substring = substr $input_str, $cursor, $chars;
+
+ if ( is_palindrome($substring) ) {
+ push @palindromes, $substring unless exists $map{$substring};
+ $map{$substring} = 1;
+ }
+
+ $chars += 1;
+ }
+ }
+
+ return join q{ }, @palindromes;
+}
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+sub trim {
+ my $string = shift;
+ $string =~ s/ \A \s+ | \s+ \z //x;
+ return $string;
+}
+
+sub parse_test_case {
+ my $filename = shift;
+
+ open my $fh, '<', $filename
+ or die "Could not open '$filename' - $!\n";
+
+ my $data = do { local $/ = undef; <$fh> };
+
+ close $fh;
+
+ my ( $input, $test ) = map { trim $_ }
+ grep { m/ \A \s* [^#] .* \z /sx; } split / \n /x, $data;
+
+ return ( $input, $test );
+}
+
+sub print_params {
+ my $params = shift;
+
+ print $params->{target}, ":\n";
+ print "Input: \$s = $params->{input}\n";
+ print "Output: $params->{test}\n";
+
+ return 1;
+}
+
+sub test_solution {
+ my $solution = shift;
+ my $params = shift;
+ my $result = $solution->( $params->{input} );
+
+ if ( $params->{test} eq $result ) {
+ print "\e[32mPassed \x{2690}\e[0m\n\n";
+ return 1;
+ }
+ print "\e[31mFailed \x{2715}\e[0m\n\n";
+ return 0;
+}
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+my $target = shift @ARGV // '../test_cases/ch-2/';
+
+if ( -e -r -f $target ) {
+ my ( $input, $test ) = parse_test_case($target);
+ my %params = (
+ input => $input,
+ target => $target,
+ test => $test,
+ );
+ print_params \%params;
+ test_solution( \&eertree, \%params );
+}
+elsif ( -e -r -d _ ) {
+ $target =~ s/ \A ( .*? ) \/? \z /$1\//x;
+
+ opendir my $dh, $target
+ or die "Could not open '$target' - $!\n";
+
+ my @entries = readdir $dh;
+
+ closedir $dh;
+
+ for my $entry ( sort @entries ) {
+ if ( $entry eq q{.} or $entry eq q{..} ) {
+ next;
+ }
+
+ my $path = $target . $entry;
+ my ( $input, $test ) = parse_test_case($path);
+ my %params = (
+ input => $input,
+ target => $path,
+ test => $test,
+ );
+ print_params \%params;
+ test_solution( \&eertree, \%params );
+ }
+}
+else {
+ print "Something went wrong. No test cases found at $target\n";
+}
diff --git a/challenge-145/iangoodnight/python/ch-1.py b/challenge-145/iangoodnight/python/ch-1.py
new file mode 100755
index 0000000000..79a8fb06fa
--- /dev/null
+++ b/challenge-145/iangoodnight/python/ch-1.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python3
+# ch-1.py
+
+# > https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+#
+# ## Task 1 > Dot Product
+# =======================
+#
+# You are given 2 arrays of the same size, `@a` and `@b`.
+#
+# Write a script to implement `Dot Product`.
+#
+# **Example:**
+# ```
+# @a = (1, 2, 3);
+# @b = (4, 5, 6);
+#
+# $dot_product = (1 * 4) + (2 * 5) + (3 * 6) => 4 + 10 + 18 => 32
+# ```
+
+import sys
+from pathlib import Path
+
+###############################################################################
+# PWC Solution ################################################################
+###############################################################################
+
+
+def dot_product(arr1=[], arr2=[]):
+ sum = 0
+ for i in range(len(arr1)):
+ sum += arr1[i] * arr2[i]
+ return sum
+
+###############################################################################
+# Utilities ###################################################################
+###############################################################################
+
+
+def nums_from_string(s):
+ numstrings = s.split(',')
+ return list(map(lambda n: int(n.strip()), numstrings))
+
+
+def parse_test_case(file_path):
+ with open(file_path) as f:
+ lines = f.read().splitlines()
+ filtered = list(filter(lambda l: not l.strip().startswith('#'), lines))
+ a, b, test = filtered
+ return [nums_from_string(a), nums_from_string(b), int(test)]
+
+
+def test_solution(solution, a, b, test):
+ result = solution(a, b)
+ if test == result:
+ print("\u001b[32mPassed \u2690\u001b[0m")
+ return True
+ else:
+ print("\u001b[31mFailed \u2715\u001b[0m")
+ return False
+
+
+def print_params(target, a, b, test):
+ print(f"{target}:")
+ print("@a = % s" % ', '.join(map(str, a)))
+ print("@b = % s" % ', '.join(map(str, b)))
+ print(f"Dot Product: {str(test)}")
+
+
+def print_and_run(target):
+ a, b, test = parse_test_case(target)
+ print_params(target, a, b, test)
+ test_solution(dot_product, a, b, test)
+ print()
+
+
+def run_test_suite(directory):
+ for child in Path(directory).iterdir():
+ if child.is_file():
+ print_and_run(child)
+
+
+###############################################################################
+# Main ########################################################################
+###############################################################################
+
+
+try:
+ target = sys.argv[1]
+except IndexError:
+ target = '../test_cases/ch-1/'
+
+path = Path(target)
+
+if path.is_file():
+ print_and_run(target)
+elif path.is_dir():
+ run_test_suite(target)
+else:
+ print(f"No tests found at: {target}")
diff --git a/challenge-145/iangoodnight/python/ch-2.py b/challenge-145/iangoodnight/python/ch-2.py
new file mode 100755
index 0000000000..2f84c74e41
--- /dev/null
+++ b/challenge-145/iangoodnight/python/ch-2.py
@@ -0,0 +1,142 @@
+#!/usr/bin/python3
+# ch-1.py
+
+# > https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+#
+# ## Task2 > Palindromic Tree
+# ===========================
+#
+# You are given a string `$s`.
+#
+# Write a script to create a `Palindromic Tree` for the given string
+#
+# I found this [blog] explaining `Palindromic Tree` in detail.
+#
+# **Example 1:**
+#
+# ```
+# Input: $s = 'redivider'
+# Output: r redivider e edivide d divid i ivi v
+# ```
+#
+# **Example 2:**
+#
+# ```
+# Input: $s = 'deific'
+# Output: d e i ifi f c
+# ```
+#
+# **Example 3:**
+#
+# ```
+# Input: $s = 'rotors'
+# Output: r rotor o oto t s
+# ```
+#
+# **Example 4:**
+#
+# ```
+# Input: $s = 'challenge'
+# Output: c h a l ll e n g
+# ```
+#
+# **Example 5:**
+#
+# ```
+# Input: $s = 'champion'
+# Output: c h a m p i o n
+# ```
+#
+# **Example 6**
+#
+# ```
+# Input: $s = 'christmas'
+# Output: c h r i s t m a
+# ```
+#
+# [blog]: https://medium.com/@alessiopiergiacomi/eertree-or-palindromic-tree-82453e75025b
+
+import sys
+from pathlib import Path
+
+###############################################################################
+# PWC Solution ################################################################
+###############################################################################
+
+
+def is_palindrome(string=''):
+ gnirts = string[::-1]
+ return gnirts == string
+
+
+def eertree(string):
+ palindromes = []
+ length = len(string)
+ for cursor in range(length):
+ for i in range(cursor, length):
+ chars = i + 1
+ substring = string[cursor:chars]
+ if substring not in palindromes and is_palindrome(substring):
+ palindromes.append(substring)
+ return ' '.join(palindromes)
+
+
+###############################################################################
+# Utilities ###################################################################
+###############################################################################
+
+
+def parse_test_case(filepath):
+ with open(filepath) as f:
+ lines = f.read().splitlines()
+ filtered = list(filter(lambda l: not l.strip().startswith('#'), lines))
+ string, test = filtered
+ return [string.strip(), test.strip()]
+
+
+def print_params(path, string, test):
+ print(f"{path}:")
+ print(f"Input: $s = {string}")
+ print(f"Output: {test}")
+
+
+def test_solution(solution, string, test):
+ result = solution(string)
+ if test == result:
+ print("\u001b[32mPassed \u2690\u001b[0m\n")
+ return True
+ else:
+ print("\u001b[31mFailed \u2715\u001b[0m\n")
+ return False
+
+
+def print_and_run(target):
+ string, test = parse_test_case(target)
+ print_params(target, string, test)
+ test_solution(eertree, string, test)
+
+
+def run_test_suite(directory):
+ for child in Path(directory).iterdir():
+ if child.is_file():
+ print_and_run(child)
+
+
+###############################################################################
+# Main ########################################################################
+###############################################################################
+
+
+try:
+ target = sys.argv[1]
+except IndexError:
+ target = '../test_cases/ch-2/'
+
+path = Path(target)
+
+if path.is_file():
+ print_and_run(target)
+elif path.is_dir():
+ run_test_suite(target)
+else:
+ print(f"No tests found at: {target}")
diff --git a/challenge-145/iangoodnight/ruby/ch-1.rb b/challenge-145/iangoodnight/ruby/ch-1.rb
new file mode 100755
index 0000000000..59b9777a5f
--- /dev/null
+++ b/challenge-145/iangoodnight/ruby/ch-1.rb
@@ -0,0 +1,81 @@
+#!/usr/bin/ruby -w
+## ch-1.rb
+
+# > https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+#
+# ## Task 1 > Dot Product
+# =======================
+#
+# You are given 2 arrays of the same size, `@a` and `@b`.
+#
+# Write a script to implement `Dot Product`.
+#
+# **Example:**
+# ```
+# @a = (1, 2, 3);
+# @b = (4, 5, 6);
+#
+# $dot_product = (1 * 4) + (2 * 5) + (3 * 6) => 4 + 10 + 18 => 32
+# ```
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+def dot_product(arr1, arr2)
+ sum = 0
+ arr1.each_with_index do |multiplicand, idx|
+ sum += multiplicand.to_f * arr2[idx].to_f
+ end
+ (sum % 1).zero? ? sum.to_i : sum
+end
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+def parse_test_case(file_path)
+ lines = File.read(file_path).split("\n").reject { |line| line.match(/^\s*#/) }
+ arr1, arr2, test = lines
+ a = arr1.split(/\s*,\s*/)
+ b = arr2.split(/\s*,\s*/)
+ [a, b, test]
+end
+
+def test_solution(callback, *args, test)
+ result = callback.call(*args)
+ if test.to_f == result.to_f
+ puts "\e[32mPassed \u2690\e[0m"
+ else
+ puts "\e[31mFailed \u2715\e[0m"
+ end
+end
+
+def print_params(target, a, b, test)
+ puts "#{target}:"
+ puts "@a = #{a.join(', ')}"
+ puts "@b = #{b.join(', ')}"
+ puts "Dot Product: #{test}"
+end
+
+def print_and_run(target)
+ a, b, test = parse_test_case(target)
+ print_params(target, a, b, test)
+ test_solution(method(:dot_product), a, b, test)
+ puts "\n"
+end
+
+def run_test_suite(dir)
+ tests = Dir.entries(dir).select { |f| File.file? File.join(dir, f) }
+ tests.sort.each do |test|
+ print_and_run(File.join(dir, test))
+ end
+end
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+target = ARGV[0] || '../test_cases/ch-1'
+print_and_run(target) if File.file?(target)
+run_test_suite(target) if File.directory?(target)
diff --git a/challenge-145/iangoodnight/ruby/ch-2.rb b/challenge-145/iangoodnight/ruby/ch-2.rb
new file mode 100755
index 0000000000..a41ea3ccc8
--- /dev/null
+++ b/challenge-145/iangoodnight/ruby/ch-2.rb
@@ -0,0 +1,125 @@
+#!/usr/bin/ruby -w
+# ch-2.rb
+
+# > https://theweeklychallenge.org/blog/perl-weekly-challenge-145/
+#
+# ## Task2 > Palindromic Tree
+# ===========================
+#
+# You are given a string `$s`.
+#
+# Write a script to create a `Palindromic Tree` for the given string
+#
+# I found this [blog] explaining `Palindromic Tree` in detail.
+#
+# **Example 1:**
+#
+# ```
+# Input: $s = 'redivider'
+# Output: r redivider e edivide d divid i ivi v
+# ```
+#
+# **Example 2:**
+#
+# ```
+# Input: $s = 'deific'
+# Output: d e i ifi f c
+# ```
+#
+# **Example 3:**
+#
+# ```
+# Input: $s = 'rotors'
+# Output: r rotor o oto t s
+# ```
+#
+# **Example 4:**
+#
+# ```
+# Input: $s = 'challe