aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Bell_West <roger@firedrake.org>2023-05-10 17:43:37 +0100
committerRoger Bell_West <roger@firedrake.org>2023-05-10 17:43:37 +0100
commitbd36304d5d9691dbe21023741457df62d71da81c (patch)
tree8d36df341efcdd5e3f1f24282bb7e7c2ee8b8bd3
parent722527ed475e56e5717e60f8d3b52d9bbcef492c (diff)
downloadperlweeklychallenge-club-bd36304d5d9691dbe21023741457df62d71da81c.tar.gz
perlweeklychallenge-club-bd36304d5d9691dbe21023741457df62d71da81c.tar.bz2
perlweeklychallenge-club-bd36304d5d9691dbe21023741457df62d71da81c.zip
RogerBW solutions for challenge no. 216
-rwxr-xr-xchallenge-216/roger-bell-west/javascript/ch-1.js76
-rwxr-xr-xchallenge-216/roger-bell-west/javascript/ch-2.js91
-rw-r--r--challenge-216/roger-bell-west/kotlin/ch-1.kt49
-rw-r--r--challenge-216/roger-bell-west/kotlin/ch-2.kt92
-rwxr-xr-xchallenge-216/roger-bell-west/lua/ch-1.lua97
-rwxr-xr-xchallenge-216/roger-bell-west/lua/ch-2.lua112
-rwxr-xr-xchallenge-216/roger-bell-west/perl/ch-1.pl35
-rwxr-xr-xchallenge-216/roger-bell-west/perl/ch-2.pl57
-rw-r--r--challenge-216/roger-bell-west/postscript/ch-1.ps178
-rw-r--r--challenge-216/roger-bell-west/postscript/ch-2.ps165
-rwxr-xr-xchallenge-216/roger-bell-west/python/ch-1.py38
-rwxr-xr-xchallenge-216/roger-bell-west/python/ch-2.py58
-rwxr-xr-xchallenge-216/roger-bell-west/raku/ch-1.p632
-rwxr-xr-xchallenge-216/roger-bell-west/raku/ch-2.p652
-rwxr-xr-xchallenge-216/roger-bell-west/ruby/ch-1.rb42
-rwxr-xr-xchallenge-216/roger-bell-west/ruby/ch-2.rb78
-rwxr-xr-xchallenge-216/roger-bell-west/rust/ch-1.rs56
-rw-r--r--challenge-216/roger-bell-west/rust/ch-2.rs84
-rw-r--r--challenge-216/roger-bell-west/tests.yaml59
19 files changed, 1451 insertions, 0 deletions
diff --git a/challenge-216/roger-bell-west/javascript/ch-1.js b/challenge-216/roger-bell-west/javascript/ch-1.js
new file mode 100755
index 0000000000..cfebc90c62
--- /dev/null
+++ b/challenge-216/roger-bell-west/javascript/ch-1.js
@@ -0,0 +1,76 @@
+#! /usr/bin/node
+
+"use strict"
+
+// 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;
+ }
+}
+
+function word2set(word) {
+ let r = new Set();
+ for (let c of word.toLowerCase()) {
+ if (c >= 'a' && c <= 'z') {
+ r.add(c);
+ }
+ }
+ return r
+}
+
+function registrationnumber(words, reg) {
+ let out = [];
+ for (let w of words) {
+ let ss = word2set(reg);
+ for (let char of word2set(w)) {
+ if (ss.has(char)) {
+ ss.delete(char);
+ if (ss.size == 0) {
+ out.push(w);
+ break;
+ }
+ }
+ }
+ }
+ return out;
+}
+
+if (deepEqual(registrationnumber(['abc', 'abcd', 'bcd'], 'AB1 2CD'), ['abcd'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (deepEqual(registrationnumber(['job', 'james', 'bjorg'], '007 JB'), ['job', 'bjorg'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (deepEqual(registrationnumber(['crack', 'road', 'rac'], 'C7 RA2'), ['crack', 'rac'])) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-216/roger-bell-west/javascript/ch-2.js b/challenge-216/roger-bell-west/javascript/ch-2.js
new file mode 100755
index 0000000000..aa8c83d535
--- /dev/null
+++ b/challenge-216/roger-bell-west/javascript/ch-2.js
@@ -0,0 +1,91 @@
+#! /usr/bin/node
+
+"use strict"
+
+function word2map(word) {
+ let m = new Map();
+ for (let c of word.toLowerCase()) {
+ if (m.has(c)) {
+ m.set(c, m.get(c) + 1);
+ } else {
+ m.set(c, 1);
+ }
+ }
+ return m;
+}
+
+function shallowclonemap(m) {
+ let mm = new Map();
+ for (let k of m.keys()) {
+ mm.set(k, m.get(k));
+ }
+ return mm;
+}
+
+function wordstickers(stickers, word) {
+ const w = word2map(word);
+ let t = shallowclonemap(w);
+ let stick = [];
+ for (let s of stickers) {
+ let f = word2map(s);
+ for (let c of f.keys()) {
+ t.delete(c);
+ }
+ stick.push(f);
+ }
+ if (t.size > 0) {
+ return 0;
+ }
+ let stack = [[w, 0]];
+ while (stack.length > 0) {
+ const st = stack.shift();
+ if (st[0].size == 0) {
+ return st[1];
+ } else {
+ const n = st[1] + 1;
+ for (let sti of stick) {
+ let sp = shallowclonemap(st[0]);
+ let v = false;
+ for (let l of sti.keys()) {
+ if (sp.has(l)) {
+ v = true;
+ const p = sp.get(l) - sti.get(l);
+ if (p > 0) {
+ sp.set(l, p);
+ } else {
+ sp.delete(l);
+ }
+ }
+ }
+ if (v) {
+ stack.push([sp, n]);
+ }
+ }
+ }
+ }
+}
+
+if (wordstickers(['perl', 'raku', 'python'], 'peon') == 2) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (wordstickers(['love', 'hate', 'angry'], 'goat') == 3) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (wordstickers(['come', 'nation', 'delta'], 'accomodation') == 4) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write(" ");
+if (wordstickers(['come', 'country', 'delta'], 'accomodation') == 0) {
+ process.stdout.write("Pass");
+} else {
+ process.stdout.write("FAIL");
+}
+process.stdout.write("\n");
diff --git a/challenge-216/roger-bell-west/kotlin/ch-1.kt b/challenge-216/roger-bell-west/kotlin/ch-1.kt
new file mode 100644
index 0000000000..5f0333a7ff
--- /dev/null
+++ b/challenge-216/roger-bell-west/kotlin/ch-1.kt
@@ -0,0 +1,49 @@
+fun word2set(word: String): MutableSet<Char> {
+ var r = mutableSetOf<Char>()
+ for (c in word.lowercase()) {
+ if (c >= 'a' && c <= 'z') {
+ r.add(c)
+ }
+ }
+ return r
+}
+
+fun registrationnumber(words: List<String>, reg: String): List<String> {
+ var out = ArrayList<String>()
+ for (w in words) {
+ var ss = word2set(reg);
+ for (char in word2set(w)) {
+ if (ss.contains(char)) {
+ ss.remove(char);
+ if (ss.size == 0) {
+ out.add(w)
+ break
+ }
+ }
+ }
+ }
+ return out
+}
+
+fun main() {
+
+ if (registrationnumber(listOf("abc", "abcd", "bcd"), "AB1 2CD") == listOf("abcd")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (registrationnumber(listOf("job", "james", "bjorg"), "007 JB") == listOf("job", "bjorg")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (registrationnumber(listOf("crack", "road", "rac"), "C7 RA2") == listOf("crack", "rac")) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-216/roger-bell-west/kotlin/ch-2.kt b/challenge-216/roger-bell-west/kotlin/ch-2.kt
new file mode 100644
index 0000000000..bd624f6ee0
--- /dev/null
+++ b/challenge-216/roger-bell-west/kotlin/ch-2.kt
@@ -0,0 +1,92 @@
+fun word2map(word: String): Map<Char, Int> {
+ var m = mutableMapOf<Char, Int>()
+ for (c in word.lowercase()) {
+ if (m.contains(c)) {
+ m.set(c, m.get(c)!! + 1)
+ } else {
+ m.set(c, 1)
+ }
+ }
+ return m
+}
+
+fun shallowclonemap(m: Map<Char, Int>): MutableMap<Char, Int> {
+ var mm = mutableMapOf<Char, Int>()
+ for (k in m.keys) {
+ mm.set(k, m.get(k)!!)
+ }
+ return mm
+}
+
+fun wordstickers(stickers: List<String>, word: String): Int {
+ var w = word2map(word)
+ var t = shallowclonemap(w)
+ var stick = ArrayList<Map<Char, Int>>()
+ for (s in stickers) {
+ val f = word2map(s)
+ for (c in f.keys) {
+ t.remove(c)
+ }
+ stick.add(f)
+ }
+ if (t.size > 0) {
+ return 0
+ }
+ var stack = arrayListOf(Pair(w, 0))
+ while (stack.size > 0) {
+ val st = stack.removeFirst()
+ if (st.first.size == 0) {
+ return st.second
+ } else {
+ val n = st.second + 1;
+ for (sti in stick) {
+ var sp = shallowclonemap(st.first)
+ var v = false
+ for (l in sti.keys) {
+ if (sp.contains(l)) {
+ v = true
+ val p = sp.get(l)!! - sti.get(l)!!
+ if (p > 0) {
+ sp.put(l, p)
+ } else {
+ sp.remove(l)
+ }
+ }
+ }
+ if (v) {
+ stack.add(Pair(sp, n))
+ }
+ }
+ }
+ }
+ return 0
+}
+
+fun main() {
+
+ if (wordstickers(listOf("perl", "raku", "python"), "peon") == 2) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (wordstickers(listOf("love", "hate", "angry"), "goat") == 3) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (wordstickers(listOf("come", "nation", "delta"), "accomodation") == 4) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ print(" ")
+ if (wordstickers(listOf("come", "country", "delta"), "accomodation") == 0) {
+ print("Pass")
+ } else {
+ print("Fail")
+ }
+ println("")
+
+}
diff --git a/challenge-216/roger-bell-west/lua/ch-1.lua b/challenge-216/roger-bell-west/lua/ch-1.lua
new file mode 100755
index 0000000000..0de0a79536
--- /dev/null
+++ b/challenge-216/roger-bell-west/lua/ch-1.lua
@@ -0,0 +1,97 @@
+#! /usr/bin/lua
+
+-- 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
+
+function deepcopy(src)
+ local dst = {}
+ for k, v in pairs(src) do
+ if type(v) == "table" then
+ v = deepcopy(v)
+ end
+ dst[k] = v
+ end
+ return dst
+end
+
+function propersize(t)
+ local l=0
+ for k,v in pairs(t) do
+ l = l + 1
+ end
+ return l
+end
+
+function word2set(word)
+ local s = {}
+ for c in string.gmatch(string.lower(word), "%a") do
+ s[c] = true
+ end
+ return s
+end
+
+function registrationnumber(words, reg)
+ local s = word2set(reg)
+ local out = {}
+ for _, w in ipairs(words) do
+ local ss = deepcopy(s)
+ for c, _ in pairs(word2set(w)) do
+ if s[c] ~= nil then
+ s[c] = nil
+ if propersize(s) == 0 then
+ table.insert(out, w)
+ break
+ end
+ end
+ end
+ end
+ return out
+end
+
+if recursive_compare(registrationnumber({"abc", "abcd", "bcd"}, "AB1 2CD"), {"abcd"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if recursive_compare(registrationnumber({"job", "james", "bjorg"}, "007 JB"), {"job", "bjorg"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if recursive_compare(registrationnumber({"crack", "road", "rac"}, "C7 RA2"), {"crack", "rac"}) then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-216/roger-bell-west/lua/ch-2.lua b/challenge-216/roger-bell-west/lua/ch-2.lua
new file mode 100755
index 0000000000..da804cb8f1
--- /dev/null
+++ b/challenge-216/roger-bell-west/lua/ch-2.lua
@@ -0,0 +1,112 @@
+#! /usr/bin/lua
+
+function keys(t)
+ local a = {}
+ for k, v in pairs(t) do
+ table.insert(a, k)
+ end
+ return a
+end
+
+function deepcopy(src)
+ local dst = {}
+ for k, v in pairs(src) do
+ if type(v) == "table" then
+ v = deepcopy(v)
+ end
+ dst[k] = v
+ end
+ return dst
+end
+
+function propersize(t)
+ local l=0
+ for k,v in pairs(t) do
+ l = l + 1
+ end
+ return l
+end
+
+function word2map(word)
+ local m = {}
+ for c in string.gmatch(string.lower(word), "%a") do
+ if m[c] == nil then
+ m[c] = 1
+ else
+ m[c] = m[c] + 1
+ end
+ end
+ return m
+end
+
+function wordstickers(stickers, word)
+ local w = word2map(word)
+ local t = deepcopy(w)
+ local stick = {}
+ for _, s in ipairs(stickers) do
+ local f = word2map(s)
+ for _, c in ipairs(keys(f)) do
+ t[c] = nil
+ end
+ table.insert(stick, f)
+ end
+ if propersize(t) > 0 then
+ return 0
+ end
+ local stack = { {w, 0} }
+ while #stack > 0 do
+ local st = table.remove(stack, 1)
+ if propersize(st[1]) == 0 then
+ return st[2]
+ else
+ local n = st[2] + 1
+ for _a, sti in ipairs(stick) do
+ local sp = deepcopy(st[1])
+ local v = false
+ for _b, l in ipairs(keys(sti)) do
+ if sp[l] ~= nil then
+ v = true
+ p = sp[l] - sti[l]
+ if p > 0 then
+ sp[l] = p
+ else
+ sp[l] = nil
+ end
+ end
+ end
+ if v then
+ table.insert(stack, {sp, n})
+ end
+ end
+ end
+ end
+end
+
+if wordstickers({"perl", "raku", "python"}, "peon") == 2 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if wordstickers({"love", "hate", "angry"}, "goat") == 3 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if wordstickers({"come", "nation", "delta"}, "accomodation") == 4 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+io.write(" ")
+
+if wordstickers({"come", "country", "delta"}, "accomodation") == 0 then
+ io.write("Pass")
+else
+ io.write("FAIL")
+end
+print("")
+
diff --git a/challenge-216/roger-bell-west/perl/ch-1.pl b/challenge-216/roger-bell-west/perl/ch-1.pl
new file mode 100755
index 0000000000..4fb06c032c
--- /dev/null
+++ b/challenge-216/roger-bell-west/perl/ch-1.pl
@@ -0,0 +1,35 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 3;
+
+is_deeply(registrationnumber(['abc', 'abcd', 'bcd'], 'AB1 2CD'), ['abcd'], 'example 1');
+is_deeply(registrationnumber(['job', 'james', 'bjorg'], '007 JB'), ['job', 'bjorg'], 'example 2');
+is_deeply(registrationnumber(['crack', 'road', 'rac'], 'C7 RA2'), ['crack', 'rac'], 'example 3');
+
+use Storable qw(dclone);
+
+sub word2set($word) {
+ return {map {$_ => 1} grep /[a-z]/, split '', lc($word)};
+}
+
+sub registrationnumber($words, $reg) {
+ my $s = word2set($reg);
+ my @out;
+ foreach my $w (@{$words}) {
+ my $ss = dclone($s);
+ foreach my $char (keys %{word2set($w)}) {
+ if (exists $ss->{$char}) {
+ delete $ss->{$char};
+ if (scalar %{$ss} == 0) {
+ push @out, $w;
+ last;
+ }
+ }
+ }
+ }
+ return \@out;
+}
diff --git a/challenge-216/roger-bell-west/perl/ch-2.pl b/challenge-216/roger-bell-west/perl/ch-2.pl
new file mode 100755
index 0000000000..2ad6af790c
--- /dev/null
+++ b/challenge-216/roger-bell-west/perl/ch-2.pl
@@ -0,0 +1,57 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use experimental 'signatures';
+
+use Test::More tests => 4;
+
+is(wordstickers(['perl', 'raku', 'python'], 'peon'), 2, 'example 1');
+is(wordstickers(['love', 'hate', 'angry'], 'goat'), 3, 'example 2');
+is(wordstickers(['come', 'nation', 'delta'], 'accomodation'), 4, 'example 3');
+is(wordstickers(['come', 'country', 'delta'], 'accomodation'), 0, 'example 4');
+
+use Storable qw(dclone);
+
+sub wordstickers($stickers, $word) {
+ my %w;
+ map {$w{$_}++} split '',lc($word);
+ my $t = dclone(\%w);
+ my @stick;
+ foreach my $s (@{$stickers}) {
+ my %f;
+ map {$f{$_}++} split '',lc($s);
+ map {delete $t->{$_}} keys %f;
+ push @stick, \%f;
+ }
+ if (scalar %{$t}) {
+ return 0;
+ }
+ my @stack = ([\%w, 0]);
+ while (scalar @stack > 0) {
+ my $st = shift @stack;
+ if (scalar %{$st->[0]} == 0) {
+ return $st->[1];
+ } else {
+ my $n = $st->[1] + 1;
+ foreach my $sti (@stick) {
+ my $sp = dclone($st->[0]);
+ my $v = 0;
+ foreach my $l (keys %{$sti}) {
+ if (exists $sp->{$l}) {
+ $v = 1;
+ my $p = $sp->{$l} - $sti->{$l};
+ if ($p > 0) {
+ $sp->{$l} = $p;
+ } else {
+ delete $sp->{$l};
+ }
+ }
+ }
+ if ($v) {
+ push @stack, [$sp, $n];
+ }
+ }
+ }
+ }
+}
diff --git a/challenge-216/roger-bell-west/postscript/ch-1.ps b/challenge-216/roger-bell-west/postscript/ch-1.ps
new file mode 100644
index 0000000000..727c189748
--- /dev/null
+++ b/challenge-216/roger-bell-west/postscript/ch-1.ps
@@ -0,0 +1,178 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/test.start {
+ print (:) print
+ /test.pass 0 def
+ /test.count 0 def
+} bind def
+
+/deepcopy {
+ 2 dict begin
+ /a exch def
+ a type /dicttype eq {
+ <<
+ a keys {
+ /k exch def
+ k
+ a k get deepcopy
+ } forall
+ >>
+ } {
+ a type /arraytype eq {
+ [
+ a {
+ deepcopy
+ } forall
+ ]
+ } {
+ a type /stringtype eq {
+ a dup length string cvs
+ } {
+ a
+ } ifelse
+ } ifelse
+ } ifelse
+ end
+} 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
+
+/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
+
+/test.end {
+ ( ) print
+ test.count 0 gt {
+ (Passed ) print
+ test.pass (...) cvs print
+ (/) print
+ test.count (...) cvs print
+ ( \() print
+ test.pass 100 mul test.count idiv (...) cvs print
+ (%\)) print
+ (\r\n) print
+ } if
+} bind def
+
+/keys { % dict -> array of dict keys
+ [ exch
+ {
+ pop
+ } forall
+ ]
+} bind def
+
+
+% end included library code
+
+/word2set {
+ 2 dict begin
+ /s 0 dict def
+ {
+ /c exch def
+ c 65 ge c 90 le and {
+ /c c 32 add def
+ } if
+ c 97 ge c 122 le and {
+ s c true put
+ } if
+ } forall
+ s
+ end
+} bind def
+
+/registrationnumber {
+ 6 dict begin
+ /reg exch def
+ /words exch def
+ /s reg word2set def
+ [
+ words {
+ /w exch def
+ /ss s deepcopy def
+ w word2set keys {
+ /char exch def
+ ss char known {
+ ss char undef
+ ss length 0 eq {
+ w
+ exit
+ } if
+ } if
+ } forall
+ } forall
+ ]
+ end
+} bind def
+
+(registrationnumber) test.start
+[(abc) (abcd) (bcd)] (AB1 2CD) registrationnumber [(abcd)] deepeq test
+[(job) (james) (bjorg)] (007 JB) registrationnumber [(job) (bjorg)] deepeq test
+[(crack) (road) (rac)] (C7 RA2) registrationnumber [(crack) (rac)] deepeq test
+test.end
diff --git a/challenge-216/roger-bell-west/postscript/ch-2.ps b/challenge-216/roger-bell-west/postscript/ch-2.ps
new file mode 100644
index 0000000000..adede24f3c
--- /dev/null
+++ b/challenge-216/roger-bell-west/postscript/ch-2.ps
@@ -0,0 +1,165 @@
+%!PS
+
+% begin included library code
+% see https://codeberg.org/Firedrake/postscript-libraries/
+/keys { % dict -> array of dict keys
+ [ exch
+ {
+ pop
+ } forall
+ ]
+} bind def
+
+/deepcopy {
+ 2 dict begin
+ /a exch def
+ a type /dicttype eq {
+ <<
+ a keys {
+ /k exch def
+ k
+ a k get deepcopy
+ } forall
+ >>
+ } {
+ a type /arraytype eq {
+ [
+ a {
+ deepcopy
+ } forall
+ ]
+ } {
+ a type /stringtype eq {
+ a dup length string cvs
+ } {
+ a
+ } ifelse
+ } ifelse
+ } ifelse
+ end
+} bind def
+
+/test.end {
+ ( ) print
+ test.count 0 gt {
+ (Passed ) print
+ test.pass (...) cvs print
+ (/) print
+ test.count (...) cvs print
+ ( \() print
+ test.pass 100 mul test.count idiv (...) cvs print
+ (%\)) print
+ (\r\n) print
+ } if
+} bind def
+
+/test.start {
+ print (:) print
+ /test.pass 0 def
+ /test.count 0 def
+} bind def
+
+/apop.left { % [a b c] -> [b c] a
+ dup 0 get exch
+ [ exch aload length -1 roll pop ] exch
+} 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
+
+/apush.right { % [a b] c -> [a b c]
+ exch
+ [ exch aload length 2 add -1 roll ]
+} bind def
+
+
+% end included library code
+
+/word2map {
+ 2 dict begin
+ /m 0 dict def
+ {
+ /c exch def
+ c 65 ge c 90 le and {
+ /c c 32 add def
+ } if
+ c 97 ge c 122 le and {
+ m c known {
+ m c m c get 1 add put
+ } {
+ m c 1 put
+ } ifelse
+ } if
+ } forall
+ m
+ end
+} bind def
+
+/wordstickers {
+ 12 dict begin
+ word2map /w exch def
+ /t w deepcopy def
+ /stick exch
+ [ exch
+ {
+ /f exch word2map def
+ f keys {
+ /c exch def
+ t c known {
+ t c undef
+ } if
+ } forall
+ f
+ } forall
+ ] def
+ t length 0 gt {
+ 0
+ } {
+ /stack [ [ w 0 ] ] def
+ {
+ stack apop.left /st exch def /stack exch def
+ st 0 get length 0 eq {
+ st 1 get
+ exit
+ } {
+ /n st 1 get 1 add def
+ stick {
+ /sti exch def
+ /sp st 0 get deepcopy def
+ /v false def
+ sti keys {
+ /l exch def
+ sp l known {
+ /v true def
+ /p sp l get sti l get sub def
+ p 0 gt {
+ sp l p put
+ } {
+ sp l undef
+ } ifelse
+ } if
+ } forall
+ v {
+ /stack stack [ sp n ] apush.right def
+ } if
+ } forall
+ } ifelse
+ } loop
+ } ifelse
+ end
+} bind def
+
+(wordstickers) test.start
+[(perl) (raku) (python)] (peon) wordstickers 2 eq test
+[(love) (hate) (angry)] (goat) wordstickers 3 eq test
+[(come) (nation) (delta)] (accomodation) wordstickers 4 eq test
+[(come) (country) (delta)] (accomodation) wordstickers 0 eq test
+test.end
diff --git a/challenge-216/roger-bell-west/python/ch-1.py b/challenge-216/roger-bell-west/python/ch-1.py
new file mode 100755
index 0000000000..adfba72f9a
--- /dev/null
+++ b/challenge-216/roger-bell-west/python/ch-1.py
@@ -0,0 +1,38 @@
+#! /usr/bin/python3
+
+from copy import deepcopy
+
+def word2set(word):
+ r = set()
+ for c in word.lower():
+ if c >= 'a' and c <= 'z':
+ r.add(c)
+ return r
+
+def registrationnumber(words, reg):
+ s = word2set(reg)
+ out = []
+ for w in words:
+ ss = deepcopy(s)
+ for char in word2set(w):
+ if char in ss:
+ ss -= set(char)
+ if len(ss) == 0:
+ out.append(w)
+ break
+ return out
+
+import unittest
+
+class TestRegistrationnumber(unittest.TestCase):
+
+ def test_ex1(self):
+ self.assertEqual(registrationnumber(["abc", "abcd", "bcd"], "AB1 2CD"), ["abcd"], 'example 1')
+
+ def test_ex2(self):
+ self.assertEqual(registrationnumber(["job", "james", "bjorg"], "007 JB"), ["job", "bjorg"], 'example 2'