PowerShell: Puzzle Night At the User Group!

My PowerShell Usergroup:

As I said before, I am a regular at my local PowerShell user group. I keenly look forward to our monthly meetings. We share a lot of PowerShell code/tips/tricks among ourselves. It is a small but close-knit group. Yesterday, one of the members, Jason Walker came up with coding puzzles for the group. This blog post is about the puzzles Jason created for solving using PowerShell. This is the kind of simple stuff that keeps the meetings interesting and fun (besides the Pizza!).

My solutions may not be the most elegant or concise but they work and were created on the fly during the meeting!

Puzzle 1: Generate Prime Numbers

The first puzzle for the night was to generate Prime numbers. As described, “prime number (or a prime) is a natural number greater than 1 that cannot be formed by multiplying two smaller natural numbers“.

The first 25 prime numbers (all the prime numbers less than 100) are:

2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97

The solution is to have two loops. The outer loop loops through all the numbers in a sequence. The inner loop loops from 2 up to the current number trying to divide it by the inner loop number. If the outer loop number is divisible with a remainder of zero, we break from the inner loop as it is a composite number and not a prime. If we have exhausted the inner loop, it is a prime!

[int[]] $allnumbers = @(1..100);
[bool] $divisible = $false;
[int] $numSub = 0;

ForEach ($num in $allnumbers)
{
   $divisible = $false;

   foreach ($numSub in @(2..$num))
   {
        if ($num -ne $numSub)
        {
            if (($num % $numSub) -eq 0) `
            {
                $divisible = $true;
                break;
            }
        }
   }

   if ($divisible -eq $false)
   {
        $numSub;
   }
}

Puzzle 2: Is Number Even – Without Any Arithmetic Operators

This puzzle requires some out-of-the box thinking as no arithmetic operators can be used (like +, -, *, /, % etc.). It clicked after Jason gave us a clue which is to convert them to Binary. To understand what I mean let us convert the numbers 1 through 25 to their binary equivalent and see the results:

@(1..25) | %{"$_ = " + [Convert]::ToString($_, 2)}

The results are:

1 = 1
2 = 10
3 = 11
4 = 100
5 = 101
6 = 110
7 = 111
8 = 1000
9 = 1001
10 = 1010
11 = 1011
12 = 1100
13 = 1101
14 = 1110
15 = 1111
16 = 10000
17 = 10001
18 = 10010
19 = 10011
20 = 10100
21 = 10101
22 = 10110
23 = 10111
24 = 11000
25 = 11001

Do you see the pattern? All even numbers end with a zero and all odd numbers end with a one. The right-most (first digit) of the binary form. The first digit is the 1’s place. The picture here should explain things better.

Once, the above is clear, the answer is simple!

[int[]] $allnumbers = @(1..100)

ForEach ($num in $allnumbers)
{
    #convert to binary and if the last digit is 0, it is a even number
    if (([Convert]::ToString($num, 2)).EndsWith(0))
    {
        $num
    }
}

This should give us the results we need

2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
....

Puzzle 3: Bi-weekly Pay Days For The Year + Bonus For Finding 3 Payment Months

Starting with a date like January 11, 2019, the goal is to find all the bi-weekly Pay Day’s for the year. You get bonus points for identifying the months with more than 2 payments!

For this one, I had two solutions. The long and mundane solution and then the concise solution.

Solution #1: The mundane one:

This does not need much explanation. We just increment the start date by 14 days and keep a counter for month to see if we have 3 of the same month in a row to print:

[Datetime]$startDate = [Datetime]'1/11/2019'
[Datetime]$endDate = [Datetime]'1/11/2019'
[int] $sameMonthCounter = 0

#First paydate
$endDate

While ($endDate -le [Datetime]'12/31/2019')
{
    $endDate = $endDate.AddDays(14)
    $endDate 

    if ($endDate.Month -eq $endDate.AddDays(-14).Month)
    {
        $sameMonthCounter++
    }
    else
    {
        $sameMonthCounter = 1
    }

    if ($sameMonthCounter -eq 3)
    {
        "******** $endDate has 3 paydays ********"
    }
}

The output is as below:

Friday, January 11, 2019 12:00:00 AM
Friday, January 25, 2019 12:00:00 AM
Friday, February 8, 2019 12:00:00 AM
Friday, February 22, 2019 12:00:00 AM
Friday, March 8, 2019 12:00:00 AM
Friday, March 22, 2019 12:00:00 AM
Friday, April 5, 2019 12:00:00 AM
Friday, April 19, 2019 12:00:00 AM
Friday, May 3, 2019 12:00:00 AM
Friday, May 17, 2019 12:00:00 AM
Friday, May 31, 2019 12:00:00 AM
******** 05/31/2019 00:00:00 has 3 paydays ********
Friday, June 14, 2019 12:00:00 AM
Friday, June 28, 2019 12:00:00 AM
Friday, July 12, 2019 12:00:00 AM
Friday, July 26, 2019 12:00:00 AM
Friday, August 9, 2019 12:00:00 AM
Friday, August 23, 2019 12:00:00 AM
Friday, September 6, 2019 12:00:00 AM
Friday, September 20, 2019 12:00:00 AM
Friday, October 4, 2019 12:00:00 AM
Friday, October 18, 2019 12:00:00 AM
Friday, November 1, 2019 12:00:00 AM
Friday, November 15, 2019 12:00:00 AM
Friday, November 29, 2019 12:00:00 AM
******** 11/29/2019 00:00:00 has 3 paydays ********
Friday, December 13, 2019 12:00:00 AM
Friday, December 27, 2019 12:00:00 AM
Friday, January 10, 2020 12:00:00 AM

Solution #2: The more elegant solution:

Here, we collect the dates into an ArrayList and then do a Group-Object to get the months with more than 3 or more pay dates.

[Datetime]$startDate = [Datetime]'1/11/2019'
[Datetime]$endDate = [Datetime]'1/11/2019'
[System.Collections.ArrayList]$ArrayList = @()

$ArrayList.Add($endDate) | Out-Null

While ($endDate -le [Datetime]'12/31/2019')
{
    $endDate = $endDate.AddDays(14)
    $ArrayList.Add($endDate) | Out-Null
}
#The results
$ArrayList
"-----------"

#Months with more than 2 payments
$ArrayList |
    %{$_.ToString('MMM')} |
    Group-Object |
    Where-Object {$_.count -gt 2} |
    Select-Object Name, Count |
    Format-Table

This again gives us the desired output

Friday, January 11, 2019 12:00:00 AM
Friday, January 25, 2019 12:00:00 AM
Friday, February 8, 2019 12:00:00 AM
Friday, February 22, 2019 12:00:00 AM
Friday, March 8, 2019 12:00:00 AM
Friday, March 22, 2019 12:00:00 AM
Friday, April 5, 2019 12:00:00 AM
Friday, April 19, 2019 12:00:00 AM
Friday, May 3, 2019 12:00:00 AM
Friday, May 17, 2019 12:00:00 AM
Friday, May 31, 2019 12:00:00 AM
Friday, June 14, 2019 12:00:00 AM
Friday, June 28, 2019 12:00:00 AM
Friday, July 12, 2019 12:00:00 AM
Friday, July 26, 2019 12:00:00 AM
Friday, August 9, 2019 12:00:00 AM
Friday, August 23, 2019 12:00:00 AM
Friday, September 6, 2019 12:00:00 AM
Friday, September 20, 2019 12:00:00 AM
Friday, October 4, 2019 12:00:00 AM
Friday, October 18, 2019 12:00:00 AM
Friday, November 1, 2019 12:00:00 AM
Friday, November 15, 2019 12:00:00 AM
Friday, November 29, 2019 12:00:00 AM
Friday, December 13, 2019 12:00:00 AM
Friday, December 27, 2019 12:00:00 AM
Friday, January 10, 2020 12:00:00 AM
-----------
Name Count
---- -----
Jan      3
May      3
Nov      3

Puzzle 4: Floyd’s Triangle With Numbers

This puzzle is to build a triangle with numbers. The first line has one number, the second line two numbers, the third line three numbers and so on. Only when you try to code this will you see that it is not as simple as it sounds. If you don’t believe me, please try.

The output should be like this:

1
2 3
4 5 6
7 8 9 10
11 12 13 14 15
16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45
46 47 48 49 50 51 52 53 54 55
56 57 58 59 60 61 62 63 64 65 66
67 68 69 70 71 72 73 74 75 76 77 78
79 80 81 82 83 84 85 86 87 88 89 90 91

The code that I created to produce it is not too complex. Basically, $line keeps track of which line number we are writing to. The line should have as many numbers printed. The counter $numsWritten keeps tracks of how many numbers have been written on the current line. Once we are done, we reset the counters and continue.

[int[]] $allnumbers = @(1..100)
[int] $line = 0
[int] $numsPerLine = 1
[int] $numsWritten = 0
[int] $totalLines  = 12

ForEach ($num in $allnumbers)
{
    Write-Host  "$num " -NoNewline
    $numsWritten++

    if($numsWritten -gt $line)
    {
        Write-Host " "
        $numsWritten = 0
        $line++

        if ($line -gt $totalLines)
        {
            break;
        }
    }
}

The output is as expected:

1
2 3
4 5 6
7 8 9 10
11 12 13 14 15
16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45
46 47 48 49 50 51 52 53 54 55
56 57 58 59 60 61 62 63 64 65 66
67 68 69 70 71 72 73 74 75 76 77 78
79 80 81 82 83 84 85 86 87 88 89 90 91

We did a more complex version of this same thing in an earlier Puzzle Night.

Hope you got something out of this as I did. Thanks for reading!

One thought on “PowerShell: Puzzle Night At the User Group!

Leave a comment