aboutsummaryrefslogtreecommitdiff
path: root/challenge-200/bob-lied/perl/ch-2.pl
blob: 18c23d62fb54bebcd518652bb979b9729743772f (plain)
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env perl
# vim:set ts=4 sw=4 sts=4 et ai wm=0 nu:
#=============================================================================
# ch-2.pl Perl Weekly Challenge Week 200 Task 2 Seven Segment 200
#=============================================================================
# Copyright (c) 2023, Bob Lied
#=============================================================================
# A seven segment display is an electronic component, usually used to
# display digits. The segments are labeled 'a' through 'g' as shown:
#   
#    --   <- a
#  f|  |b
#    --   <- g
#  e|  |c
#    --   <- d
#
# The encoding of each digit can be represented compactly as a truth table:
# 
# my @truth = qw<abcdef bc abdeg abcdg bcfg acdfg acdefg abc abcdefg abcfg>;
# 
# For example, $truth[1] = ‘bc’. The digit 1 would have segments ‘b’ and ‘c’
# enabled.
# 
# Write a program that accepts any decimal number and draws that number
# as # a horizontal sequence of ASCII seven segment displays, similar to
# the # following:
#
# -------  -------  -------
#       |  |     |  |     |
#       |  |     |  |     |
# -------
# |        |     |  |     |
# |        |     |  |     |
# -------  -------  -------
#
# To qualify as a seven segment display, each segment must be drawn (or not
# drawn) according to your @truth table.
# The number "200" was of course chosen to celebrate our 200th week!
#=============================================================================

use v5.36;

# Which segments are on for each digit?
             #    0    1   2     3    4     5     6     7     8      9
my @truth = qw<abcdef bc abdeg abcdg bcfg acdfg acdefg abc abcdefg abcfg>;

# 7x7 as one 49-character string, a few different styles
# aaaaaaa     aaaaa          aaaaa
# f     f    f     b        ff   bb  Bold version
# f     b    f     b        ff   bb
# ggggggg     ggggg          ggggg
# e     c    e     c        ee   cc
# e     c    e     c        ee   cc
# ddddddd     ddddd          ddddd
#my $Digit = 'aaaaaaaf     bf     bggggggge     ce     cddddddd'; # Match example
 my $Digit = ' aaaaa f     bf     b ggggg e     ce     c ddddd '; # Rounded corners
#my $Digit = 'aaaaaaaff   bbff   bbgggggggee   ccee   ccddddddd'; # Bold

# Display characteristics
 my %Char;
#$Char{$_} = '-' for qw(a d g); $Char{$_} = '|' for qw(b c e f);
#$Char{$_} = '=' for qw(a d g); $Char{$_} = '|' for qw(b c e f);
#$Char{$_} = '#' for qw(a b c d e f g);
#$Char{$_} = 'o' for qw(a b c d e f g);

# Use Unicode horizontal and vertical bars
binmode(STDOUT, "encoding(UTF-8)"); # No "wide character" warning
#$Char{$_} = "\x{2500}" for qw(a d g); $Char{$_} = "\x{2502}" for qw(b c e f);
 $Char{$_} = "\x{2501}" for qw(a d g); $Char{$_} = "\x{2503}" for qw(b c e f);
# $Char{$_} = "\x{2550}" for qw(a d g); $Char{$_} = "\x{2551}" for qw(b c e f);

use Getopt::Long;
my $Verbose = 0;
my $DoTest  = 0;

GetOptions("test" => \$DoTest, "verbose" => \$Verbose);
exit(!runTest()) if $DoTest;

displayAsSegment($_) for @ARGV;

sub displayAsSegment($str)
{
    # Make a 49-character segment string for each digit
    my @seg = map { asSegment($_) } split '', $str;

    for my $row ( 0 .. 6 )
    {
        # Make a row by taking a 7-char substring of each segment
        # Top row:    0 ..  6     0 ..  6     0 ..  6
        #       2:    7    13     7 .. 13     7 .. 13
        #   ...
        #       7:   42 .. 48    42 .. 48    42 .. 48
        say join("  ", map { substr($seg[$_], $row*7, 7) } 0 .. $#seg);
    }
}

sub asSegment($digit)
{
    my $seven = $Digit;
    my $t = $truth[$digit];

    # Turn off segments not in the digit
    $seven =~ s/[^$t]/ /g;

    for my $on ( split '', $t )
    {
        $seven =~ s/$on/$Char{$on}/g;
    }
    return $seven;
}

sub runTest
{
    use Test2::V0;

            my $Digit = ' aaaaa f     bf     b ggggg e     ce     c ddddd ';
    is( asSegment("0"), ' ----- |     ||     |       |     ||     | ----- ', "asSegment 0");
    is( asSegment("1"), '             |      |             |      |       ', "asSegment 1");
    is( asSegment("2"), ' -----       |      | ----- |      |       ----- ', "asSegment 2");
    is( asSegment("3"), ' -----       |      | -----       |      | ----- ', "asSegment 3");
    is( asSegment("4"), '       |     ||     | -----       |      |       ', "asSegment 4");
    is( asSegment("5"), ' ----- |      |       -----       |      | ----- ', "asSegment 5");
    is( asSegment("6"), ' ----- |      |       ----- |     ||     | ----- ', "asSegment 6");
    is( asSegment("7"), ' -----       |      |             |      |       ', "asSegment 7");
    is( asSegment("8"), ' ----- |     ||     | ----- |     ||     | ----- ', "asSegment 8");
    is( asSegment("9"), ' ----- |     ||     | -----       |      |       ', "asSegment 9");

    done_testing;
}