Seven Segments to Midnight

Wherein we write the numbers of the world, secret and public, sacred and profane, along the strata that separate the Earth from the Sky…

THE WEEKLY CHALLENGE – PERL & RAKU #200 Task 2


“All the world’s a stage,

And all the men and women merely players;

They have their exits and their entrances;

And one man in his time plays many parts,

His acts being seven ages.”

— William Shakespeare


Seven Segment 200

Submitted by: Ryan J Thompson

A seven segment display is an electronic component, usually used to display digits. The segments are labeled 'a' through 'g' as shown:

Seven Segment

The encoding of each digit can thus 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!

Background

Despite its obvious connection to the information age, and Ryan’s necessary simplification, the seven-segment display predates electronic computing pretty much entirely. I think it should be instead better thought of as an exercise in “parsimony of information transfer” — how to visually represent the ten modern numeric digits using a minimum number of separate elements to draw them. In this sense we can look at the development of the displays as progressing alongside that of the computing used to drive them — as coexisting in the same problem-space. Computing naturally adopted and refined the preexisting idea into an electronic component, rather than creating it whole-cloth from scratch.

As a visual signal, the seven-segment display is a natural extension of a type of mechanical semaphore: using a collection of elements set at fixed positions, by altering the patterns of activated and unactivated components individual messages are transmitted from the system as a whole, when we “draw” a complete numeral all at once. This is contrasted to temporal encodings, for instance Morse code, where a stream of simpler signals are varied through time until enough information is sent to reconstruct a complete letter.

In a classic optical semaphore flags are raised and lowered in certain positions; here segments on a grid are either shown or not. With each signal a complete symbol, more complex messages can then be built-up by changing the patterns over time to form sequential sentences of related information, transmitting letters or numbers one character at a time, or by referencing any predetermined specialized code to determine meaning.

The most familiar form of the seven-segment display would once have been the ubiquitous LED readouts found in digital clocks, but other technologies have largely replaced these by now. The backlight behind a liquid crystal matrix produces a cleaner image that’s more pleasing to the eye, while still only requiring the simplest data to select the numbers to present. It’s also worth noting that light is not required for this display at all: I once owned a passively lit digital clock that used a system of movable flaps to show each segment as either the white or black side of a small card. The four digit positions were fixed on the clock face with a permanent divider placed between two sets of two digits, displaying the hours and minutes. This is in contrast to another weird mechanical digital clock I once owned, that worked using a two flip-books of 12 and 60 cards. These were printed in two groups for hours and minutes, and rotating drums placed another card in place every appropriate hour or minute using a mechanism much like a kinescope.

Mechanically complicated as these devices were, and hardly new when I eventually acquired them (probably from a thrift store), eventually both met the end of their long and overly complicated lives. It seems various flip-clocks are available today, as are the more complicated moving seven-segment ones, although I cannot find the quite old version, probably from the 1970s, that I originally acquired. That one was wild. They probably all broke years ago.

But I digress.

The display for the seven-segment mechanical clock was driven by solenoid motors and thin wires to flip the cards over. Obviously with a whole separate mechanical armature for each segment, and four numerals required to display the time, having only seven segments per digit was a good plan. I don’t recall whether the leftmost position had flippers to show more than the the digits “1”, “2” and a blank space for showing single-digit hours. As the clock display was a crazy constructed thing, not build from off-the-shelf parts one digit at a time, I suspect not. With standardized part production, it’s almost always more cost-effective now to include a display capable of showing any arbitrary digit, even it the expected output is limited to a subset of its capabilities.

The seven-segment LED display, after all, became ubiquitous because it was an integrated circuit that could be inexpensively mass-produced. That was the whole idea.

Analysis

Given seven elements, the digits 0 through 9 can be represented by selecting specific patterns of elements to display. As noted, the individual elements are commonly labeled using the characters “a” through “g”, and thus any combination of elements to activate can be encoded into a string of characters using the letters to select:

    0 => abcdef 
    1 => bc 
    2 => abdeg 
    3 => abcdg 
    4 => bcfg 
    5 => acdfg 
    6 => acdefg 
    7 => abc 
    8 => abcdefg 
    9 => abcfg 

The actual truth table describing the individual encodings could be any representation of symbols representing each segment within a pattern, but it appears we are required to use this specific mapping of numbers to letters.

METHOD

It occurred to me there may be more concise ways to pack the encoding data for the numerals, such as an array with seven elements. This, for example, would be a very literal modeling. Alternately we could strip things down completely and use a sequence of 7 bits to do the work, assigning each digit a value in the range 0-127, with each set bit in the underlying binary representation determining whether to show its corresponding element. With 27, or 128 options, we can see that we can express far more than the ten digit states required to present numbers, but we won’t concern ourselves with that here. We will mention that for devices intended for numeric display often an eighth element is added, for a decimal point in the lower right, as no element of the seven lighted alone serves this role very clearly. These are, however, still commonly referred to as seven segment displays. Actual eight-segment displays are their own weird diversion that produced half-height “0”s. For some reason I remember those half-height “0”s. I think

Various nine, fourteen and sixteen digit displays have also been produced over the years.

But I digress, again.

I do that.

PERL 5 SOLUTION

We are required to use the truth table given, which encodes to strings of letters. So be it. As such, it seems fitting to use a very Perlish solution, which is to use regular expressions to parse the strings and map them to the output.

The output itself requires five lines to span the vertical space. From top to bottom, these cover the segments “a”, “f” and “b”, “g”, “e” and “c”, and on the lowest level “d”. The top, middle and lower levels have only a single element to be reproduced, but the middle bands have variations of none, one, the other or both illuminated. Each encoded digit needs to be parsed for each line, so the /r switch is employed to return a copy of the substitution without overwriting the original.

About that overwriting, the conversion to a simulated seven segments by replacing the encoded string at the output line level by a sequence of four characters, with each number written five characters high and four wide. The conversion is done by looking for a pattern that will always match but may or may not match a value into a specific group. Depending on the results the display element is drawn using four characters (with surrounding space for the horizontals) or four blank spaces are inserted. We need the /e switch to do the required evaluation.

To accommodate the variations in the middle bands, we have to get a little clever: by matching two groups with either one or no instances, if a group contains a character a vertical bar is substituted and if not a space is placed instead.

One oddity is that because the letters are assigned clockwise and listed alphabetically, the element “c” is actually the second on the line, to the right of “f”. So if we match “c” first we end up writing to $1, which is the left bar. To settle this we reverse the returned output from the substitution to flip the order back around correctly.

The results were pretty nice, but as the output was less specified, I thought to improve things by using the heavyweight box drawing Unicode symbols for my elements. Much nicer. I found using two heavy horizontals for the “g” center bar, but aa combination of a right and left half bar characters butted together for the top and bottoms made for a more pleasing, rounded effect.

I’m sure that left to my own devices I would expand the encoding to include connecting corners or something, box drawing until the cows come home, but that wouldn’t really be simulating a seven-segment display now, would it?

use warnings;
use strict;
use utf8;
use feature ":5.26";
use feature qw(signatures);
no warnings 'experimental::signatures';
use open ':std', ':encoding(UTF-8)';

## required given encoding method
my @truth = qw( abcdef bc abdeg abcdg bcfg acdfg acdefg abc abcdefg abcfg );

## input
my $num = shift @ARGV // 1234567890;
my @out;

for my $d (split //, $num) {
    $out[0] .= $truth[$d]          =~ s/(a?)[bcdefg]*/   $1 ? ' ╺╸ ' : '    '/er;
    $out[1] .= reverse $truth[$d]  =~ s/a?(b?)[cde]*(f?)g?/
                                              ($1 ? '┃' : ' ') 
                                            . '  ' 
                                            . ($2 ? '┃' : ' ')/er;
    $out[2] .= $truth[$d]          =~ s/[abcdef]*(g?)/   $1 ? ' ━━ ' : '    '/er;
    $out[3] .= reverse $truth[$d]  =~ s/a?b?(c?)d?(e?)f?g?/
                                              ($1 ? '┃' : ' ') 
                                            . '  ' 
                                            . ($2 ? '┃' : ' ')/er;
    $out[4] .= $truth[$d]          =~ s/[abc]*(d?)[efg]*/$1 ? ' ╺╸ ' : '    '/er;
    
    $out[$_] .= '  ' for (0..4); ## spaces between digits
}

## output
for my $i (0..4) {
    say $out[$i];
}

Here, then, is the result, showing Pi to eight places, without a decimal point. Minimalist, but I like it.

Happy bicentennial, Weekly Challenge!

Jan 21, 2023 at 11:55:57 PM
~/Code/PWC/seven.pl
--------------------------------------------------------------------------------
 ╺╸                      ╺╸    ╺╸    ╺╸    ╺╸   
   ┃     ┃  ┃  ┃     ┃  ┃     ┃  ┃     ┃  ┃     
 ━━          ━━          ━━    ━━    ━━    ━━   
   ┃     ┃     ┃     ┃     ┃     ┃  ┃     ┃  ┃  
 ╺╸                      ╺╸          ╺╸    ╺╸   


The Perl Weekly Challenge, that idyllic glade wherein we stumble upon the holes for these sweet descents, is now known as

The Weekly Challenge – Perl and Raku

It is the creation of the lovely Mohammad Sajid Anwar and a veritable swarm of contributors from all over the world, who gather, as might be expected, weekly online to solve puzzles. Everyone is encouraged to visit, learn and contribute at

https://theweeklychallenge.org