diff options
| -rw-r--r-- | challenge-126/andinus/README | 206 | ||||
| -rw-r--r-- | challenge-126/andinus/README.org | 144 | ||||
| -rw-r--r-- | challenge-126/andinus/blog-1.txt | 1 | ||||
| -rw-r--r-- | challenge-126/andinus/blog-2.txt | 1 | ||||
| -rw-r--r-- | challenge-126/andinus/c/ch-1.c | 32 | ||||
| -rw-r--r-- | challenge-126/andinus/raku/ch-1.raku | 5 | ||||
| -rw-r--r-- | challenge-126/andinus/raku/ch-2.raku | 79 | ||||
| -rw-r--r-- | challenge-126/andinus/raku/input | 5 |
8 files changed, 365 insertions, 108 deletions
diff --git a/challenge-126/andinus/README b/challenge-126/andinus/README index 796b1d2086..535d2258c5 100644 --- a/challenge-126/andinus/README +++ b/challenge-126/andinus/README @@ -1,39 +1,44 @@ ━━━━━━━━━━━━━━━ - CHALLENGE 122 + CHALLENGE 126 Andinus ━━━━━━━━━━━━━━━ - 2021-07-21 + 2021-08-19 Table of Contents ───────────────── -Task 1 - Average of Stream -Task 2 - Basketball Points +Task 1 - Count Numbers +Task 2 - Minesweeper Game -Task 1 - Average of Stream -══════════════════════════ +Task 1 - Count Numbers +══════════════════════ - You are given a stream of numbers, `@N'. + You are given a positive integer `$N'. - Write a script to print the average of the stream at every point. + Write a script to print count of numbers from 1 to `$N' that don’t + contain digit 1. Example: ┌──── - │ Input: @N = (10, 20, 30, 40, 50, 60, 70, 80, 90, ...) - │ Output: 10, 15, 20, 25, 30, 35, 40, 45, 50, ... + │ Input: $N = 15 + │ Output: 8 │ - │ Average of first number is 10. - │ Average of first 2 numbers (10+20)/2 = 15 - │ Average of first 3 numbers (10+20+30)/3 = 20 - │ Average of first 4 numbers (10+20+30+40)/4 = 25 and so on. + │ There are 8 numbers between 1 and 15 that don't contain digit 1. + │ 2, 3, 4, 5, 6, 7, 8, 9. + │ + │ Input: $N = 25 + │ Output: 13 + │ + │ There are 13 numbers between 1 and 25 that don't contain digit 1. + │ 2, 3, 4, 5, 6, 7, 8, 9, 20, 22, 23, 24, 25. └──── @@ -42,18 +47,12 @@ Raku • Program: <file:raku/ch-1.raku> - The subroutine `avg' takes a list of numbers and returns their - average. We just loop over keys of `@nums' and print the average upto - each point. + Loop over `1..$N' and grep for numbers that don't have `1'. `hyper' + hints the compiler that this can be run in parallel. ┌──── - │ #| return average of lists. - │ sub avg(*@list) { (sum @list) / @list.elems; } - │ - │ #| average of stream at every point. - │ sub MAIN(*@nums where {$_.all ~~ Int}) { - │ put @nums.keys.map({avg @nums[0..$_]}); - │ } + │ print .join: ', ' with (1..$N).hyper.grep(*.comb.grep(1).elems == 0); + │ put '.'; └──── @@ -62,61 +61,72 @@ C • Program: <file:c/ch-1.c> - `argv' holds the input & `argc' holds the number of inputs. We loop - over `argv' and convert each input to an integer and add it to `sum' - which holds the sum of inputs upto that point and print `sum / idx', - `idx' being the index of input. + `argv' holds the input & `argc' holds the number of inputs. The input + should be a single integer so `argc' should be equal to 2. After + checking for that, we check if valid integer was passed. + + After checks, we loop over all numbers from 1 to `num' (passed value) + and check if they contain digit `1'. The check is performed by + converting `num' to a string and matching for '1' in the string. If we + find it then we set the flag to false and don't print the number, + otherwise we print it. ┌──── - │ long sum = 0; - │ for (int idx = 1; idx < argc; idx++) { - │ int num; - │ const char *errstr; - │ num = strtonum(argv[idx], INT_MIN, INT_MAX, &errstr); - │ if (errstr != NULL) - │ errx(1, "number is %s: %s", errstr, argv[idx]); + │ if (argc != 2) { + │ puts("Usage: ./ch-1 <number>"); + │ exit(0); + │ } │ - │ sum += num; - │ printf("%ld ", sum / idx); + │ const char *errstr; + │ int num = strtonum(argv[1], 1, INT_MAX, &errstr); + │ if (errstr != NULL) + │ errx(1, "number is %s: %s", errstr, argv[1]); + │ + │ for (int idx = 1; idx <= num; idx++) { + │ bool take = true; + │ + │ int str_size = 1 + snprintf(NULL, 0, "%d", idx); + │ char *num_str = calloc(str_size, sizeof(char)); + │ snprintf(num_str, str_size, "%d", idx); + │ + │ for (int x = 0; num_str[x] != '\0'; x++) + │ if (num_str[x] == '1') take = false; + │ free(num_str); + │ + │ if (take == true) printf("%d ", idx); │ } │ printf("\n"); └──── -Task 2 - Basketball Points -══════════════════════════ +Task 2 - Minesweeper Game +═════════════════════════ - You are given a score `$S'. + You are given a rectangle with points marked with either x or *. + Please consider the x as a land mine. - You can win basketball points e.g. 1 point, 2 points and 3 points. + Write a script to print a rectangle with numbers and x as in the + Minesweeper game. - Write a script to find out the different ways you can score `$S'. + A number in a square of the minesweeper game indicates the + number of mines within the neighbouring squares (usually + 8), also implies that there are no bombs on that square. Example: ┌──── - │ Input: $S = 4 - │ Output: 1 1 1 1 - │ 1 1 2 - │ 1 2 1 - │ 1 3 - │ 2 1 1 - │ 2 2 - │ 3 1 + │ Input: + │ x * * * x * x x x x + │ * * * * * * * * * x + │ * * * * x * x * x * + │ * * * x x * * * * * + │ x * * * x * * * * x │ - │ Input: $S = 5 - │ Output: 1 1 1 1 1 - │ 1 1 1 2 - │ 1 1 2 1 - │ 1 1 3 - │ 1 2 1 1 - │ 1 2 2 - │ 1 3 1 - │ 2 1 1 1 - │ 2 1 2 - │ 2 2 1 - │ 2 3 - │ 3 1 1 - │ 3 2 + │ Output: + │ x 1 0 1 x 2 x x x x + │ 1 1 0 2 2 4 3 5 5 x + │ 0 0 1 3 x 3 x 2 x 2 + │ 1 1 1 x x 4 1 2 2 2 + │ x 1 1 3 x 2 0 0 1 x └──── @@ -125,52 +135,32 @@ Raku • Program: <file:raku/ch-2.raku> - `(0, 1, 2, 3) xx $score' creates the list `0..3', `$score' number of - times. And `[X]' creates cross product from those lists. - - • Note: It's multipled `$score' number of times because `(1) xx - $score' is the maximum upto which we get `$score', after that the - sum will exceed `$score', we do have 0's there which means we'll get - more matches but we've already covered those cases. + `@rect' holds the challenge grids and `@grids' holds the solution. We + loop over every cell and if it's not a land mine then we loop over + it's neighbors and find the number of neighboring land mines and set + it's value. - Say the score is 3. We have 3 lists like these: + The neighbors is returned by `neighbors' subroutine which is adapted + from `Octans::Neighbors' (projects/octans) which was adapted from my + 2020 AoC day 11's solution. ┌──── - │ 0 0 0 - │ 1 1 1 - │ 2 2 2 - │ 3 3 3 - └──── - - And cross product will return: - - ┌──── - │ 0, 0, 0 - │ 0, 0, 1 - │ 0, 0, 2 - │ 0, 0, 3 - │ 0, 1, 0 - │ ... - │ 3, 3, 3 - └──── - - We loop over what the cross product returns and take the list if the - sum of all elements equals to the score. - - ┌──── - │ #| scoring basketball points - │ unit sub MAIN(Int $score); + │ my @rect = $input.IO.lines.map(*.words.cache); + │ die "Not rectangle" unless [==] @rect.map(*.elems); │ - │ .put for gather for [X] ((0, 1, 2, 3) xx $score) -> @scores { - │ take @scores if ([+] @scores) == $score; - │ }.map(*.grep(* !== 0).join).unique.map(*.comb); + │ my @grid; + │ for 0 .. @rect.end -> $r { + │ for 0 .. @rect[$r].end -> $c { + │ given @rect[$r][$c] { + │ when "x" { @grid[$r][$c] = @rect[$r][$c] } + │ when "*" { + │ @grid[$r][$c] = 0; + │ for neighbors(@rect, $r, $c).List -> $pos { + │ @grid[$r][$c]++ if @rect[$pos[0]][$pos[1]] eq "x"; + │ } + │ } + │ } + │ } + │ } + │ .put for @grid; └──── - - After we gather the lists of scores, remove 0's from there and then we - remove duplicate entries. Duplicates entries are removed by converting - them to string, using `unique' method and converting them back to Int. - - These entries occur because cross product includes them multiple - times. For example, for a score of 3: Cross product will return `0 1 - 2' and `1 2 0', both of which will satisty the condition and we'll - gather them, after removing the 0's, they become duplicates. diff --git a/challenge-126/andinus/README.org b/challenge-126/andinus/README.org new file mode 100644 index 0000000000..d9408ac98b --- /dev/null +++ b/challenge-126/andinus/README.org @@ -0,0 +1,144 @@ +#+title: Challenge 126 +#+date: 2021-08-19 +#+html_link_up: ../index.html +#+export_file_name: index +#+setupfile: ~/.emacs.d/org-templates/level-2.org + +* Task 1 - Count Numbers + +You are given a positive integer ~$N~. + +Write a script to print count of numbers from 1 to ~$N~ that don’t contain +digit 1. + +Example: +#+begin_src +Input: $N = 15 +Output: 8 + + There are 8 numbers between 1 and 15 that don't contain digit 1. + 2, 3, 4, 5, 6, 7, 8, 9. + +Input: $N = 25 +Output: 13 + + There are 13 numbers between 1 and 25 that don't contain digit 1. + 2, 3, 4, 5, 6, 7, 8, 9, 20, 22, 23, 24, 25. +#+end_src + +** Raku + +- Program: [[file:raku/ch-1.raku]] + +Loop over ~1..$N~ and grep for numbers that don't have ~1~. ~hyper~ hints the +compiler that this can be run in parallel. + +#+begin_src raku +print .join: ', ' with (1..$N).hyper.grep(*.comb.grep(1).elems == 0); +put '.'; +#+end_src + +** C + +- Program: [[file:c/ch-1.c]] + +~argv~ holds the input & ~argc~ holds the number of inputs. The input should +be a single integer so ~argc~ should be equal to 2. After checking for +that, we check if valid integer was passed. + +After checks, we loop over all numbers from 1 to ~num~ (passed value) and +check if they contain digit ~1~. The check is performed by converting ~num~ +to a string and matching for '1' in the string. If we find it then we +set the flag to false and don't print the number, otherwise we print it. + +#+begin_src c +if (argc != 2) { + puts("Usage: ./ch-1 <number>"); + exit(0); + } + +const char *errstr; +int num = strtonum(argv[1], 1, INT_MAX, &errstr); +if (errstr != NULL) + errx(1, "number is %s: %s", errstr, argv[1]); + +for (int idx = 1; idx <= num; idx++) { + bool take = true; + + int str_size = 1 + snprintf(NULL, 0, "%d", idx); + char *num_str = calloc(str_size, sizeof(char)); + snprintf(num_str, str_size, "%d", idx); + + for (int x = 0; num_str[x] != '\0'; x++) + if (num_str[x] == '1') take = false; + free(num_str); + + if (take == true) printf("%d ", idx); + } +printf("\n"); +#+end_src + +* Task 2 - Minesweeper Game + +You are given a rectangle with points marked with either x or *. Please +consider the x as a land mine. + +Write a script to print a rectangle with numbers and x as in the +Minesweeper game. + +#+begin_quote +A number in a square of the minesweeper game indicates the number of +mines within the neighbouring squares (usually 8), also implies that +there are no bombs on that square. +#+end_quote + +Example: +#+begin_src +Input: + x * * * x * x x x x + * * * * * * * * * x + * * * * x * x * x * + * * * x x * * * * * + x * * * x * * * * x + +Output: + x 1 0 1 x 2 x x x x + 1 1 0 2 2 4 3 5 5 x + 0 0 1 3 x 3 x 2 x 2 + 1 1 1 x x 4 1 2 2 2 + x 1 1 3 x 2 0 0 1 x +#+end_src + +** Raku + +- Program: [[file:raku/ch-2.raku]] + +~@rect~ holds the challenge grids and ~@grids~ holds the solution. We loop +over every cell and if it's not a land mine then we loop over it's +neighbors and find the number of neighboring land mines and set it's +value. + +The neighbors is returned by ~neighbors~ subroutine which is adapted from +~Octans::Neighbors~ (projects/octans) which was adapted from my 2020 AoC +day 11's solution. + +#+begin_src raku +my @rect = $input.IO.lines.map(*.words.cache); +die "Not rectangle" unless [==] @rect.map(*.elems); + +my @grid; +for 0 .. @rect.end -> $r { + for 0 .. @rect[$r].end -> $c { + given @rect[$r][$c] { + when "x" { @grid[$r][$c] = @rect[$r][$c] } + when "*" { + @grid[$r][$c] = 0; + for neighbors(@rect, $r, $c).List -> $pos { + @grid[$r][$c]++ if @rect[$pos[0]][$pos[1]] eq "x"; + } + } + } + } +} +.put for @grid; +#+end_src diff --git a/challenge-126/andinus/blog-1.txt b/challenge-126/andinus/blog-1.txt new file mode 100644 index 0000000000..3225883855 --- /dev/null +++ b/challenge-126/andinus/blog-1.txt @@ -0,0 +1 @@ +https://andinus.tilde.institute/pwc/challenge-126/ diff --git a/challenge-126/andinus/blog-2.txt b/challenge-126/andinus/blog-2.txt new file mode 100644 index 0000000000..3225883855 --- /dev/null +++ b/challenge-126/andinus/blog-2.txt @@ -0,0 +1 @@ +https://andinus.tilde.institute/pwc/challenge-126/ diff --git a/challenge-126/andinus/c/ch-1.c b/challenge-126/andinus/c/ch-1.c new file mode 100644 index 0000000000..c946cfab65 --- /dev/null +++ b/challenge-126/andinus/c/ch-1.c @@ -0,0 +1,32 @@ +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <limits.h> + +int main(int argc, char *argv[]) { + if (argc != 2) { + puts("Usage: ./ch-1 <number>"); + exit(0); + } + + const char *errstr; + int num = strtonum(argv[1], 1, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "number is %s: %s", errstr, argv[1]); + + for (int idx = 1; idx <= num; idx++) { + bool take = true; + + int str_size = 1 + snprintf(NULL, 0, "%d", idx); + char *num_str = calloc(str_size, sizeof(char)); + snprintf(num_str, str_size, "%d", idx); + + for (int x = 0; num_str[x] != '\0'; x++) + if (num_str[x] == '1') take = false; + free(num_str); + + if (take == true) printf("%d ", idx); + } + printf("\n"); +} diff --git a/challenge-126/andinus/raku/ch-1.raku b/challenge-126/andinus/raku/ch-1.raku new file mode 100644 index 0000000000..f5b873b95b --- /dev/null +++ b/challenge-126/andinus/raku/ch-1.raku @@ -0,0 +1,5 @@ +#| natural numbers that don't contain digit 1 +sub MAIN(UInt $N where * > 1) { + print .join: ', ' with (1..$N).hyper.grep(*.comb.grep(1).elems == 0); + put '.'; +} diff --git a/challenge-126/andinus/raku/ch-2.raku b/challenge-126/andinus/raku/ch-2.raku new file mode 100644 index 0000000000..a580082f04 --- /dev/null +++ b/challenge-126/andinus/raku/ch-2.raku @@ -0,0 +1,79 @@ +#| minesweeper game +sub MAIN(Str $input where *.IO.f = "input") { + my @rect = $input.IO.lines.map(*.words.cache); + die "Not rectangle" unless [==] @rect.map(*.elems); + + my @grid; + for 0 .. @rect.end -> $r { + for 0 .. @rect[$r].end -> $c { + given @rect[$r][$c] { + when "x" { @grid[$r][$c] = @rect[$r][$c] } + when "*" { + @grid[$r][$c] = 0; + for neighbors(@rect, $r, $c).List -> $pos { + @grid[$r][$c]++ if @rect[$pos[0]][$pos[1]] eq "x"; + } + } + } + } + } + .put for @grid; +} + +#| neighbors returns the neighbors of given index. Neighbors are +#| cached in @neighbors array. This way we don't have to compute them +#| everytime neighbors subroutine is called for the same position. +sub neighbors( + @puzzle, Int $y, Int $x --> List +) is export { + # @directions is holding a list of directions we can move in. It's + # used later for neighbors subroutine. + # $y, $x + state List @directions = (( +1, +0 ), # bottom + ( +1, +1 ), # bottom-right + ( +1, -1 ), # bottom-left + ( -1, +0 ), # top + ( -1, +1 ), # top-right + ( -1, -1 ), # top-left + ( +0, +1 ), # right + ( +0, -1 ), # left + ); + + # @neighbors holds the neighbors of given position. + state Array @neighbors; + + if @puzzle[$y][$x] { + # Don't re-compute neighbors. + unless @neighbors[$y][$x] { + # Set it to an empty array because otherwise if it has no + # neighbors then it would've be recomputed everytime + # neighbors() was called. + @neighbors[$y][$x] = []; + + my Int $pos-x; + my Int $pos-y; + + # Starting from the intital position of $y, $x we move to + # each direction according to the values specified in + # @directions array. In this case we're just trying to + # move in 4 directions (top, bottom, left & right). + direction: for @directions -> $direction { + $pos-y = $y + $direction[0]; + $pos-x = $x + $direction[1]; + + # If movement in this direction is out of puzzle grid + # boundary then move on to next direction. + next direction unless @puzzle[$pos-y][$pos-x]; + + # If neighbors exist in this direction then add them + # to @neighbors[$y][$x] array. + push @neighbors[$y][$x], [$pos-y, $pos-x]; + } + } + } else { + # If it's out of boundary then return no neighbor. + @neighbors[$y][$x] = []; + } + + return @neighbors[$y][$x]; +} diff --git a/challenge-126/andinus/raku/input b/challenge-126/andinus/raku/input new file mode 100644 index 0000000000..e211219443 --- /dev/null +++ b/challenge-126/andinus/raku/input @@ -0,0 +1,5 @@ +x * * * x * x x x x +* * * * * * * * * x +* * * * x * x * x * +* * * x x * * * * * +x * * * x * * * * x |
