aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Bell_West <roger@firedrake.org>2025-08-19 12:16:29 +0100
committerRoger Bell_West <roger@firedrake.org>2025-08-19 12:16:29 +0100
commitb3d35809f9e728cd1e057ccc7980e0d77f6970f4 (patch)
tree288ec39d893dfcadd4dd24be4294a9b76efaf1a3
parent72115ca8dee469436857991c88d7002158725057 (diff)
downloadperlweeklychallenge-club-b3d35809f9e728cd1e057ccc7980e0d77f6970f4.tar.gz
perlweeklychallenge-club-b3d35809f9e728cd1e057ccc7980e0d77f6970f4.tar.bz2
perlweeklychallenge-club-b3d35809f9e728cd1e057ccc7980e0d77f6970f4.zip
RogerBW solutions for challenge no. 335
-rwxr-xr-xchallenge-335/roger-bell-west/crystal/ch-1.cr55
-rwxr-xr-xchallenge-335/roger-bell-west/crystal/ch-2.cr63
-rwxr-xr-xchallenge-335/roger-bell-west/javascript/ch-1.js101
-rwxr-xr-xchallenge-335/roger-bell-west/javascript/ch-2.js73
-rw-r--r--challenge-335/roger-bell-west/kotlin/ch-1.kt70
-rw-r--r--challenge-335/roger-bell-west/kotlin/ch-2.kt76
-rwxr-xr-xchallenge-335/roger-bell-west/lua/ch-1.lua123
-rwxr-xr-xchallenge-335/roger-bell-west/lua/ch-2.lua85
-rwxr-xr-xchallenge-335/roger-bell-west/perl/ch-1.pl43
-rwxr-xr-xchallenge-335/roger-bell-west/perl/ch-2.pl56
-rw-r--r--challenge-335/roger-bell-west/postscript/ch-1.ps261
-rw-r--r--challenge-335/roger-bell-west/postscript/ch-2.ps167
-rwxr-xr-xchallenge-335/roger-bell-west/python/ch-1.py52
-rwxr-xr-xchallenge-335/roger-bell-west/python/ch-2.py53
-rwxr-xr-xchallenge-335/roger-bell-west/raku/ch-1.p639
-rwxr-xr-xchallenge-335/roger-bell-west/raku/ch-2.p654
-rwxr-xr-xchallenge-335/roger-bell-west/ruby/ch-1.rb63
-rwxr-xr-xchallenge-335/roger-bell-west/ruby/ch-2.rb72
-rwxr-xr-xchallenge-335/roger-bell-west/rust/ch-1.rs58
-rwxr-xr-xchallenge-335/roger-bell-west/rust/ch-2.rs111
-rw-r--r--challenge-335/roger-bell-west/scala/ch-1.scala74
-rw-r--r--challenge-335/roger-bell-west/scala/ch-2.scala86
-rw-r--r--challenge-335/roger-bell-west/tests.json76
-rw-r--r--challenge-335/roger-bell-west/typst/ch-1.typ58
-rw-r--r--challenge-335/roger-bell-west/typst/ch-2.typ72
25 files changed, 2041 insertions, 0 deletions
diff --git a/challenge-335/roger-bell-west/crystal/ch-1.cr b/challenge-335/roger-bell-west/crystal/ch-1.cr
new file mode 100755
index 0000000000..d16cb98692
--- /dev/null
+++ b/challenge-335/roger-bell-west/crystal/ch-1.cr
@@ -0,0 +1,55 @@
+#! /usr/bin/crystal
+
+def counterify(a)
+ cc = Hash(Char, Int32).new(default_value: 0)
+ a.each do |x|
+ cc[x] += 1
+ end
+ return cc
+end
+
+def commoncharacters(a)
+ mc = Hash(Char, Int32).new
+ first = true
+ a.each do |s|
+ mk = counterify(s.chars)
+ if first
+ mc = mk
+ first = false
+ else
+ mc.keys.each do |k|
+ if mk.has_key?(k)
+ mc[k] = [mc[k], mk[k]].min
+ else
+ mc.delete(k)
+ end
+ end
+ end
+ end
+ out = Array(String).new
+ mc.keys.sort.each do |c|
+ 1.upto(mc[c]) do
+ out.push(c.to_s)
+ end
+ end
+ out
+end
+
+require "spec"
+describe "commoncharacters" do
+ it "test_ex1" do
+ commoncharacters(["bella", "label", "roller"]).should eq ["e", "l", "l"]
+ end
+ it "test_ex2" do
+ commoncharacters(["cool", "lock", "cook"]).should eq ["c", "o"]
+ end
+ it "test_ex3" do
+ commoncharacters(["hello", "world", "pole"]).should eq ["l", "o"]
+ end
+ it "test_ex4" do
+ commoncharacters(["abc", "def", "ghi"]).should eq Array(String).new
+ end
+ it "test_ex5" do
+ commoncharacters(["aab", "aac", "aaa"]).should eq ["a", "a"]
+ end
+end
diff --git a/challenge-335/roger-bell-west/crystal/ch-2.cr b/challenge-335/roger-bell-west/crystal/ch-2.cr
new file mode 100755
index 0000000000..29405bd9d8
--- /dev/null
+++ b/challenge-335/roger-bell-west/crystal/ch-2.cr
@@ -0,0 +1,63 @@
+#! /usr/bin/crystal
+
+def findwinner(a)
+ board = [
+ [ 0, 0, 0 ],
+ [ 0, 0, 0 ],
+ [ 0, 0, 0 ]
+ ]
+ player = 1
+ a.each do |play|
+ board[play[0]][play[1]] = player
+ player = 3 - player
+ end
+ [
+ [0, 0, 1, 0],
+ [0, 1, 1, 0],
+ [0, 2, 1, 0],
+ [0, 0, 0, 1],
+ [1, 0, 0, 1],
+ [2, 0, 0, 1],
+ [0, 0, 1, 1],
+ [0, 2, 1, -1],
+ ].each do |pattern|
+ cellvals = Set(Int32).new
+ 0.upto(2) do |i|
+ x = pattern[0] + i * pattern[2]
+ y = pattern[1] + i * pattern[3]
+ cellvals.add(board[y][x])
+ end
+ if cellvals.size == 1
+ winner = cellvals.to_a[0]
+ if winner == 1
+ return "A"
+ elsif winner == 2
+ return "B"
+ end
+ end
+ end
+ if a.size == 9
+ return "Draw"
+ else
+ return "Pending"
+ end
+end
+
+require "spec"
+describe "findwinner" do
+ it "test_ex1" do
+ findwinner([[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]).should eq "A"
+ end
+ it "test_ex2" do
+ findwinner([[0, 0], [1, 1], [0, 1], [0, 2], [1, 0], [2, 0]]).should eq "B"
+ end
+ it "test_ex3" do
+ findwinner([[0, 0], [1, 1], [2, 0], [1, 0], [1, 2], [2, 1], [0, 1], [0, 2], [2, 2]]).should eq "Draw"
+ end
+ it "test_ex4" do
+ findwinner([[0, 0], [1, 1]]).should eq "Pending"
+ end
+ it "test_ex5" do
+ findwinner([[1, 1], [0, 0], [2, 2], [0, 1], [1, 0], [0, 2]]).should eq "B"
+ end
+end
diff --git a/challenge-335/roger-bell-west/javascript/ch-1.js b/challenge-335/roger-bell-west/javascript/ch-1.js
new file mode 100755
index 0000000000..7c002de222
--- /dev/null
+++ b/challenge-335/roger-bell-west/javascript/ch-1.js
@@ -0,0 +1,101 @@
+#! /usr/bin/node
+
+"use strict"
+
+function commoncharacters(a) {
+ let mc = new Map;
+ let first = true;
+ for (let s of a) {
+ const mk = counterify(s.split(""));
+ if (first) {
+ mc = mk;
+ first = false;
+ } else {
+ for (let k of mc.keys()) {
+ if (mk.has(k)) {
+ mc.set(k, Math.min(mc.get(k), mk.get(k)));
+ } else {
+ mc.delete(k);
+ }
+ }
+ }
+ }
+ let out = [];
+ let kl = [...mc.keys()];
+ kl.sort();
+ for (let c of kl) {
+ for (let n = 1; n <= mc.get(c); n++) {
+ out.push(c);
+ }
+ }
+ return out;
+}
+
+function counterify(a) {
+ let cc = new Map;
+ for (let x of a) {
+ if (!cc.has(x)) {
+ cc.set(x, 0);
+ }
+ cc.set(x, cc.get(x) + 1);
+ }
+ return cc;
+}
+
+// by Frank Tan
+// https://stackoverflow.com/questions/38400594/javascript-deep-comparison
+function deepEqual(a,b)
+{
+ if( (typeof a == 'object' && a != null) &&
+ (typeof b == 'object' && b != null) )
+ {
+ var count = [0,0];
+ for( var key in a) count[0]++;
+ for( var key in b) count[1]++;
+ if( count[0]-count[1] != 0) {return false;}
+ for( var key in a)
+ {
+ if(!(key in b) || !deepEqual(a[key],b[key])) {return false;}
+ }
+ for( var key in b)
+ {
+ if(!(key in a) || !deepEqual(b[key],a[key])) {return false;}
+ }
+ return true;
+ }
+ else
+ {
+ return a === b;
+ }
+}
+
+if (deepEqual(commoncharacters(['bella', 'label', 'roller']), ['e', 'l', 'l'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (deepEqual(commoncharacters(['cool', 'lock', 'cook']), ['c', 'o'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (deepEqual(commoncharacters(['hello', 'world', 'pole']), ['l', 'o'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (deepEqual(commoncharacters(['abc', 'def', 'ghi']), [])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (deepEqual(commoncharacters(['aab', 'aac', 'aaa']), ['a', 'a'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-335/roger-bell-west/javascript/ch-2.js b/challenge-335/roger-bell-west/javascript/ch-2.js
new file mode 100755
index 0000000000..a3eda21565
--- /dev/null
+++ b/challenge-335/roger-bell-west/javascript/ch-2.js
@@ -0,0 +1,73 @@
+#! /usr/bin/node
+
+"use strict"
+
+function findwinner(a) {
+ let board = [ [0, 0, 0], [0, 0, 0], [0, 0, 0] ];
+ let player = 1;
+ for (let play of a) {
+ board[play[0]][play[1]] = player;
+ player = 3 - player;
+ }
+ for (let pattern of [
+ [0, 0, 1, 0],
+ [0, 1, 1, 0],
+ [0, 2, 1, 0],
+ [0, 0, 0, 1],
+ [1, 0, 0, 1],
+ [2, 0, 0, 1],
+ [0, 0, 1, 1],
+ [0, 2, 1, -1]
+ ]) {
+ let cellvals = new Set;
+ for (let i = 0; i <= 2; i++) {
+ const x = pattern[0] + i * pattern[2];
+ const y = pattern[1] + i * pattern[3];
+ cellvals.add(board[y][x]);
+ }
+ if (cellvals.size == 1) {
+ let winner = [...cellvals][0];
+ if (winner == 1) {
+ return "A";
+ } else if (winner == 2) {
+ return "B";
+ }
+ }
+ }
+ if (a.length == 9) {
+ return "Draw";
+ } else {
+ return "Pending";
+ }
+}
+
+if (findwinner([[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]) == 'A') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (findwinner([[0, 0], [1, 1], [0, 1], [0, 2], [1, 0], [2, 0]]) == 'B') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (findwinner([[0, 0], [1, 1], [2, 0], [1, 0], [1, 2], [2, 1], [0, 1], [0, 2], [2, 2]]) == 'Draw') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (findwinner([[0, 0], [1, 1]]) == 'Pending') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (findwinner([[1, 1], [0, 0], [2, 2], [0, 1], [1, 0], [0, 2]]) == 'B') {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-335/roger-bell-west/kotlin/ch-1.kt b/challenge-335/roger-bell-west/kotlin/ch-1.kt
new file mode 100644
index 0000000000..b63b668434
--- /dev/null
+++ b/challenge-335/roger-bell-west/kotlin/ch-1.kt
@@ -0,0 +1,70 @@
+fun counterify(a: List<Char>): Map<Char, Int> {
+ var cc = mutableMapOf<Char, Int>().withDefault({0})
+ for (x in a) {
+ cc.set(x, cc.getValue(x) + 1)
+ }
+ return cc
+}
+
+fun commoncharacters(a: List<String>): List<String> {
+ var mc = mutableMapOf<Char, Int>().withDefault({0})
+ var first = true
+ for (s in a) {
+ val mk = counterify(s.toCharArray().toList())
+ if (first) {
+ mc = mk.toMutableMap()
+ first = false
+ } else {
+ for (k in mc.keys.toList()) {
+ if (mk.contains(k)) {
+ mc.set(k, listOf(mc.getValue(k), mk.getValue(k)).minOrNull()!!)
+ } else {
+ mc.remove(k)
+ }
+ }
+ }
+ }
+ var out = ArrayList<String>()
+ for (c in mc.keys.sorted()) {
+ val s = c.toString()
+ for (n in 1 .. mc.getValue(c)) {
+ out.add(s)
+ }
+ }
+ return out.toList()
+}
+
+fun main() {
+
+ if (commoncharacters(listOf("bella", "label", "roller")) == listOf("e", "l", "l")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (commoncharacters(listOf("cool", "lock", "cook")) == listOf("c", "o")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (commoncharacters(listOf("hello", "world", "pole")) == listOf("l", "o")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (commoncharacters(listOf("abc", "def", "ghi")) == emptyList<String>()) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (commoncharacters(listOf("aab", "aac", "aaa")) == listOf("a", "a")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-335/roger-bell-west/kotlin/ch-2.kt b/challenge-335/roger-bell-west/kotlin/ch-2.kt
new file mode 100644
index 0000000000..2cb9878611
--- /dev/null
+++ b/challenge-335/roger-bell-west/kotlin/ch-2.kt
@@ -0,0 +1,76 @@
+fun findwinner(a: List<List<Int>>): String {
+ var board = ArrayList(listOf(ArrayList(listOf(0, 0, 0)),
+ ArrayList(listOf(0, 0, 0)),
+ ArrayList(listOf(0, 0, 0))
+ ))
+ var player = 1
+ for (play in a) {
+ board[play[0]][play[1]] = player
+ player = 3 - player
+ }
+ for (pattern in listOf(
+ listOf(0, 0, 1, 0),
+ listOf(0, 1, 1, 0),
+ listOf(0, 2, 1, 0),
+ listOf(0, 0, 0, 1),
+ listOf(1, 0, 0, 1),
+ listOf(2, 0, 0, 1),
+ listOf(0, 0, 1, 1),
+ listOf(0, 2, 1, -1)
+ )) {
+ var cellvals = mutableSetOf<Int>()
+ for (i in 0 .. 2) {
+ val x = pattern[0] + i * pattern[2]
+ val y = pattern[1] + i * pattern[3]
+ cellvals.add(board[y][x])
+ }
+ if (cellvals.size == 1) {
+ var winner = cellvals.toList()[0]
+ if (winner == 1) {
+ return "A"
+ } else if (winner == 2) {
+ return "B"
+ }
+ }
+ }
+ if (a.size == 9) {
+ return "Draw"
+ } else {
+ return "Pending"
+ }
+}
+
+fun main() {
+
+ if (findwinner(listOf(listOf(0, 0), listOf(2, 0), listOf(1, 1), listOf(2, 1), listOf(2, 2))) == "A") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (findwinner(listOf(listOf(0, 0), listOf(1, 1), listOf(0, 1), listOf(0, 2), listOf(1, 0), listOf(2, 0))) == "B") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (findwinner(listOf(listOf(0, 0), listOf(1, 1), listOf(2, 0), listOf(1, 0), listOf(1, 2), listOf(2, 1), listOf(0, 1), listOf(0, 2), listOf(2, 2))) == "Draw") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (findwinner(listOf(listOf(0, 0), listOf(1, 1))) == "Pending") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (findwinner(listOf(listOf(1, 1), listOf(0, 0), listOf(2, 2), listOf(0, 1), listOf(1, 0), listOf(0, 2))) == "B") {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-335/roger-bell-west/lua/ch-1.lua b/challenge-335/roger-bell-west/lua/ch-1.lua
new file mode 100755
index 0000000000..200f658fec
--- /dev/null
+++ b/challenge-335/roger-bell-west/lua/ch-1.lua
@@ -0,0 +1,123 @@
+#! /usr/bin/lua
+
+function commoncharacters(a)
+ local mc = {}
+ for sn, s in ipairs(a) do
+ local mk = counterify(split(s))
+ if sn == 1 then
+ mc = mk
+ else
+ for k, _v in pairs(mc) do
+ if mk[k] ~= nil then
+ mc[k] = math.min(mc[k], mk[k])
+ else
+ mc[k] = nil
+ end
+ end
+ end
+ end
+ local out = {}
+ local kl = keys(mc)
+ table.sort(kl)
+ for _, c in ipairs(kl) do
+ for n = 1, mc[c] do
+ table.insert(out, c)
+ end
+ end
+ return out
+end
+
+function keys(t)
+ local a = {}
+ for k, v in pairs(t) do
+ table.insert(a, k)
+ end
+ return a
+end
+
+function counterify(a)
+ local cc = {}
+ for _, c in ipairs(a) do
+ if cc[c] == nil then
+ cc[c] = 0
+ end
+ cc[c] = cc[c] + 1
+ end
+ return cc
+end
+
+function split(t)
+ local cl = {}
+ string.gsub(t,
+ "(.)",
+ function(c)
+ table.insert(cl, c)
+ end
+ )
+ return cl
+end
+
+-- by Michael Anderson at
+-- https://stackoverflow.com/questions/8722620/comparing-two-index-tables-by-index-value-in-lua
+-- modified by Roger
+function recursive_compare(t1,t2)
+ -- Use usual comparison first.
+ if t1==t2 then return true end
+ -- We only support non-default behavior for tables
+ if (type(t1)~="table") then return false end
+ -- They better have the same metatables
+ local mt1 = getmetatable(t1)
+ local mt2 = getmetatable(t2)
+ if( not recursive_compare(mt1,mt2) ) then return false end
+ -- Build list of all keys
+ local kk = {}
+ for k1, _ in pairs(t1) do
+ kk[k1] = true
+ end
+ for k2, _ in pairs(t2) do
+ kk[k2] = true
+ end
+ -- Check each key that exists in at least one table
+ for _, k in ipairs(kk) do
+ if (not recursive_compare(t1[k], t2[k])) then
+ return false
+ end
+ end
+ return true
+end
+
+if recursive_compare(commoncharacters({"bella", "label", "roller"}), {"e", "l", "l"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if recursive_compare(commoncharacters({"cool", "lock", "cook"}), {"c", "o"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if recursive_compare(commoncharacters({"hello", "world", "pole"}), {"l", "o"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if recursive_compare(commoncharacters({"abc", "def", "ghi"}), {}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if recursive_compare(commoncharacters({"aab", "aac", "aaa"}), {"a", "a"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-335/roger-bell-west/lua/ch-2.lua b/challenge-335/roger-bell-west/lua/ch-2.lua
new file mode 100755
index 0000000000..573112c886
--- /dev/null
+++ b/challenge-335/roger-bell-west/lua/ch-2.lua
@@ -0,0 +1,85 @@
+#! /usr/bin/lua
+
+function findwinner(a)
+ local board = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }
+ local player = 1
+ for _, play in ipairs(a) do
+ board[1 + play[1]][1 + play[2]] = player
+ player = 3 - player
+ end
+ for _, pattern in ipairs({
+ {0, 0, 1, 0},
+ {0, 1, 1, 0},
+ {0, 2, 1, 0},
+ {0, 0, 0, 1},
+ {1, 0, 0, 1},
+ {2, 0, 0, 1},
+ {0, 0, 1, 1},
+ {0, 2, 1, -1}
+ }) do
+ local cellvals = {}
+ for i = 0, 2 do
+ local x = 1 + pattern[1] + i * pattern[3]
+ local y = 1 + pattern[2] + i * pattern[4]
+ cellvals[board[y][x]] = true
+ end
+ local w = keys(cellvals)
+ if #w == 1 then
+ local winner = w[1]
+ if winner == 1 then
+ return "A"
+ elseif winner == 2 then
+ return "B"
+ end
+ end
+ end
+ if #a == 9 then
+ return "Draw"
+ else
+ return "Pending"
+ end
+end
+
+function keys(t)
+ local a = {}
+ for k, v in pairs(t) do
+ table.insert(a, k)
+ end
+ return a
+end
+
+if findwinner({{0, 0}, {2, 0}, {1, 1}, {2, 1}, {2, 2}}) == "A" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if findwinner({{0, 0}, {1, 1}, {0, 1}, {0, 2}, {1, 0}, {2, 0}}) == "B" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if findwinner({{0, 0}, {1, 1}, {2, 0}, {1, 0}, {1, 2}, {2, 1}, {0, 1}, {0, 2}, {2, 2}}) == "Draw" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if findwinner({{0, 0}, {1, 1}}) == "Pending" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if findwinner({{1, 1}, {0, 0}, {2, 2}, {0, 1}, {1, 0}, {0, 2}}) == "B" then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-335/roger-bell-west/perl/ch-1.pl b/challenge-335/roger-bell-west/perl/ch-1.pl
new file mode 100755
index 0000000000..e8c55e6a0c
--- /dev/null
+++ b/challenge-335/roger-bell-west/perl/ch-1.pl
@@ -0,0 +1,43 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 5;
+
+is_deeply(commoncharacters(['bella', 'label', 'roller']), ['e', 'l', 'l'], 'example 1');
+is_deeply(commoncharacters(['cool', 'lock', 'cook']), ['c', 'o'], 'example 2');
+is_deeply(commoncharacters(['hello', 'world', 'pole']), ['l', 'o'], 'example 3');
+is_deeply(commoncharacters(['abc', 'def', 'ghi']), [], 'example 4');
+is_deeply(commoncharacters(['aab', 'aac', 'aaa']), ['a', 'a'], 'example 5');
+
+use List::Util qw(min);
+
+sub commoncharacters($a) {
+ my %mc;
+ my $first = 1;
+ foreach my $s (@{$a}) {
+ my %mk;
+ map {$mk{$_}++} split '',$s;
+ if ($first) {
+ %mc = %mk;
+ $first = 0;
+ } else {
+ foreach my $k (keys %mc) {
+ if (exists $mk{$k}) {
+ $mc{$k} = min($mc{$k}, $mk{$k});
+ } else {
+ delete $mc{$k};
+ }
+ }
+ }
+ }
+ my @out;
+ foreach my $c (sort keys %mc) {
+ foreach (1 .. $mc{$c}) {
+ push @out, $c;
+ }
+ }
+ \@out;
+}
diff --git a/challenge-335/roger-bell-west/perl/ch-2.pl b/challenge-335/roger-bell-west/perl/ch-2.pl
new file mode 100755
index 0000000000..bf5f0f13cc
--- /dev/null
+++ b/challenge-335/roger-bell-west/perl/ch-2.pl
@@ -0,0 +1,56 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 5;
+
+is(findwinner([[0, 0], [2, 0], [1, 1], [2, 1], [2, 2]]), 'A', 'example 1');
+is(findwinner([[0, 0], [1, 1], [0, 1], [0, 2], [1, 0], [2, 0]]), 'B', 'example 2');
+is(findwinner([[0, 0], [1, 1], [2, 0], [1, 0], [1, 2], [2, 1], [0, 1], [0, 2], [2, 2]]), 'Draw', 'example 3');
+is(findwinner([[0, 0], [1, 1]]), 'Pending', 'example 4');
+is(findwinner([[1, 1], [0, 0], [2, 2], [0, 1], [1, 0], [0, 2]]), 'B', 'example 5');
+
+sub findwinner($a) {
+ my @board = (
+ [ 0, 0, 0 ],
+ [ 0, 0, 0 ],
+ [ 0, 0, 0 ],
+ );
+ my $player = 1;
+ foreach my $play (@{$a}) {
+ $board[$play->[0]][$play->[1]] = $player;
+ $player = 3 - $player;
+ }
+ foreach my $pattern (
+ [0, 0, 1, 0],
+ [0, 1, 1, 0],
+ [0, 2, 1, 0],
+ [0, 0, 0, 1],
+ [1, 0, 0, 1],
+ [2, 0, 0, 1],
+ [0, 0, 1, 1],
+ [0, 2, 1, -1],
+ ) {
+ my %cellvals;
+ foreach my $i (0 .. 2) {
+ my $x = $pattern->[0] + $i * $pattern->[2];
+ my $y = $pattern->[1] + $i * $pattern->[3];
+ $cellvals{$board[$y][$x]}++;
+ }
+ if (scalar keys %cellvals == 1) {
+ my $winner = (keys %cellvals)[0];
+ if ($winner == 1) {
+ return "A";
+ } elsif ($winner == 2) {
+ return "B";
+ }
+ }
+ }
+ if (scalar @{$a} == 9) {
+ return "Draw";
+ } else {
+ return "Pending";
+ }
+}
diff --git a/challenge-335/roger-bell-west/postscript/ch-1.ps b/challenge-335/roger-bell-west/postscript/ch-1.ps
new file mode 100644
index 0000000000..b70b315685
--- /dev/null
+++ b/challenge-335/roger-bell-west/postscript/ch-1.ps
@@ -0,0 +1,261 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/a2s {
+ 2 dict begin
+ /i exch def
+ i length dup string /o exch def
+ 1 sub 0 exch 1 exch {
+ dup i 3 -1 roll get o 3 1 roll put
+ } for
+ o
+ end
+} bind def
+
+/s2a {
+ [ exch { } forall ]
+} bind def
+
+/quicksort.main { % lo hi -> (null)
+ 3 dict begin
+ /hi exch def
+ /lo exch def
+ /xit false def
+ lo 0 lt {
+ /xit true def
+ } if
+ hi 0 lt {
+ /xit true def
+ } if
+ lo hi ge {
+ /xit true def
+ } if
+ xit not {
+ /p quicksort.partition def
+ lo p quicksort.main
+ p 1 add hi quicksort.main
+ } if
+ end
+} bind def
+
+/quicksort.with_comparator { % [ a c b ] { comparator } -> [ a b c ]
+ 2 dict begin
+ /cmp exch def
+ /arr exch def
+ arr length 0 gt {
+ 0 arr length 1 sub quicksort.main
+ } if
+ arr
+ end
+} bind def
+
+/test.start {
+ print (:) print
+ /test.pass 0 def
+ /test.count 0 def
+} bind def
+
+/keys { % dict -> array of dict keys
+ [ exch
+ {
+ pop
+ } forall
+ ]
+} bind def
+
+/deepeq {
+ 2 dict begin
+ /a exch def
+ /b exch def
+ a type b type eq {
+ a type /dicttype eq {
+ a length b length eq {
+ <<
+ a {
+ pop
+ true
+ } forall
+ b {
+ pop
+ true
+ } forall
+ >>
+ true exch
+ {
+ pop
+ dup a exch known {
+ dup b exch known {
+ dup a exch get exch b exch get deepeq not {
+ pop false
+ } if
+ } {
+ false
+ } ifelse
+ } {
+ false
+ } ifelse
+ } forall
+ } {
+ false
+ } ifelse
+ } {
+ a type dup /arraytype eq exch /stringtype eq or {
+ a length b length eq {
+ true
+ 0 1 a length 1 sub {
+ dup a exch get exch b exch get deepeq not {
+ pop false
+ exit
+ } if
+ } for
+ } {
+ false
+ } ifelse
+ } {
+ a b eq
+ } ifelse
+ } ifelse
+ } {
+ false
+ } ifelse
+ end
+} bind def
+
+/quicksort.cmp {
+ 2 copy
+ lt {
+ pop pop -1
+ } {
+ gt {
+ 1
+ } {
+ 0
+ } ifelse
+ } ifelse
+} bind def
+
+/test {
+ /test.count test.count 1 add def
+ {
+ /test.pass test.pass 1 add def
+ } {
+ ( ) print
+ test.count (....) cvs print
+ (-fail) print
+ } ifelse
+} bind def
+
+/quicksort {
+ { quicksort.cmp } quicksort.with_comparator
+} bind def
+
+/quicksort.partition {
+ 3 dict begin
+ /pivot arr hi lo add 2 idiv get def
+ /i lo 1 sub def
+ /j hi 1 add def
+ {
+ {
+ /i i 1 add def
+ arr i get pivot cmp 0 ge {
+ exit
+ } if
+ } loop
+ {
+ /j j 1 sub def
+ arr j get pivot cmp 0 le {
+ exit
+ } if
+ } loop
+ i j ge {
+ j
+ exit
+ } if
+ i j quicksort.swap
+ } loop
+ end