Assume you have a single 7-segment display without a decimal point, so 7 "lines" that can be labelled A through G as seen here.

a labeled 7-segment display

This display will only show the numbers from 0 to 9 as usual, like so:

7-segment numbers visual representation

Any time this display changes from one number to another, some of its lights will have to change their state from on to off or vice versa, or they can simply stay on, e.g. in the case of 1 -> 2, segment B can stay on, while segments A, G, E, D need to turn on, and C needs to turn off; for the change 6 -> 8, only segment B needs to be changed from off to on.


Your task is to take a sequence of numbers as reasonably formatted input and output the amount of on and off changes as the display shows each number in the sequence in input order. You should output a separate score for "on" and "off" in a reasonable format. A light that stays on between numbers shall not be counted towards either on or off. The initial state of the display is all lights off, so the first number will always count as only "on" changes. The final state will be the last number displayed.

Test cases

123 -> 7, 2 (7 on, 2 off)
111 -> 2, 0 (2 segments on, then no changes)
987 -> 7, 4
2468 -> 11, 4 (thanks to @ovs for catching a mistake here!)

This is , so the shortest code wins. Also, this is my first post here, so I hope I made no mistakes for my submission :)


10 Answers 10


APL (Dyalog Extended), 34 bytes

Anonymous tacit prefix function taking a list of numbers. Requires 0-based indexing (⎕IO←0).


Try it online!

 change list into column matrix

'~0my3[_p␡{'⊇ map to ASCII characters with corresponding pit pattern

11⎕DR get corresponding binary Data Representation as 8-column table

0⍪ prepend an all-zero row

2() apply the following tacit function with 2 as left argument:

>⌿ indicate where a 1 is above a 0 (lit. greater-than reduction over vertical windows of size 2)

<⌿,⍥() concatenate with where-0-is-above-1 as follows:

   list of indices where there are 1s

   tally the indices (this gives the count of 1s)


05AB1E, 40 35 bytes


-5 bytes thanks to @Steffan

Try it online or verify all test cases.


•z]∍1:pIªy• # Push compressed integer 1098931065668123279355
  2в        # Convert it to base-2 as list: [1,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1]
    7ô      # Split it into parts of size 7: [[1,1,1,0,1,1,1],[0,0,1,0,0,1,0],[1,0,1,1,1,0,1],[1,0,1,1,0,1,1],[0,1,1,1,0,1,0],[1,1,0,1,0,1,1],[1,1,0,1,1,1,1],[1,0,1,0,0,1,0],[1,1,1,1,1,1,1],[1,1,1,1,0,1,1]]
D           # Duplicate it
 Iн         # Push the first digit of the input
   è        # Use it to index into this list of lists
    (       # Negate all 1s to -1s in the list
s           # Swap so the duplicated list is at the top again
 ã          # Create all possible pairs using the cartesian power with itself
  €ø        # Zip/transpose each inner pair of lists; swapping rows/columns
    Æ       # Reduce each inner-most pair by subtracting
     I      # Push the input-integer again
      ü2    # Create overlapping pairs of it
        è   # Use that to index into the other list
«           # Merge the two lists together
 ˜          # Flatten it
  ®1‚       # Push pair [-1,1]
     ¢      # Count the amount of -1s and 1s in the list
            # (after which this pair is output implicitly as result)

See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •z]∍1:pIªy• is 1098931065668123279355 and •z]∍1:pIªy•2в is [1,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1].

  • 1
    \$\begingroup\$ 35 bytes? \$\endgroup\$
    – naffetS
    Commented Jun 2, 2022 at 16:27

JavaScript (Node.js), 92 bytes

Expects a list of digits. Returns [off, on].

Contains some unprintable characters.


Try it online!


Given the segment bitmask \$x\$ of the previous digit and the segment bitmask \$q\$ of the new digit, we compute \$k=x\operatorname{XOR}q\$ and use the helper function \$g\$ to update the off and on counters which are stored in the array \$o[\:]\$:

g = k =>       // k = result of the XOR between the 2 bitmasks
k &&           // stop when k = 0, which actually happens because
               // of arithmetic underflow because we use k / 2
               // rather than k >> 1 to save a byte
g(             // otherwise, do a recursive call:
  k / 2,       //   pass k / 2 for the next iteration
  o[q & 1] +=  //   select the counter according to the LSB of q
    k & 1,     //   increment it if the LSB of k is set
  q /= 2       //   divide q by 2
)              // end of recursive call

JavaScript (ES6), 99 bytes

An alternate version without Buffer().

The order of the segment bits and the XOR value were chosen to optimize the overall length of the lookup code.


Try it online!

  • 1
    \$\begingroup\$ -2 bytes for your second answer using '}!Ws+z~1\x7F'.charCodeAt(n) \$\endgroup\$ Commented Jun 2, 2022 at 23:21
  • 2
    \$\begingroup\$ @MatthewJensen You're right. But the purpose of my 2nd version was to optimize the lookup with an array of numbers. (Maybe I should have said without a string rather than without Buffer().) \$\endgroup\$
    – Arnauld
    Commented Jun 3, 2022 at 6:22

Rust, 147 bytes

|a|{let(b,mut e,f)=(b"~0my3[_p{",0,|a,b:u8,c:u8|a+(b^c&b).count_ones());a.iter().fold((0,0),|(l,r),&d|{let c=(f(l,b[d],e),f(r,e,b[d]));e=b[d];c})}

Try it online!

Note that the space in the byte string is really the ASCII delete character (code point 0x7f). The basic idea is that the set bits are stored as a mask packed in a byte string, and the changed bits are detected with some bit twiddling, similar to the other answers from practical langs (although not the same- we've all taken different approaches for the same general technique, at least so far).


PARI/GP, 107 bytes

-4 bytes and fixed a bug thanks to @Arnauld.


Attempt This Online!


Python, 116 112 bytes

-4 bytes thanks to @Kevin Cruijssen

def f(s):
    for b in s:B=b'~0my3[_p\x7f{'[b];X+=(~A&B).bit_count();Y+=(A&~B).bit_count();A=B

Attempt This Online!

  • 3
    \$\begingroup\$ Both bin(...).count("1") can be (...).bit_count() for -4 bytes. \$\endgroup\$ Commented Jun 2, 2022 at 18:09

C (gcc), 136 134 bytes

#define B+=__builtin_popcount((d^c)
i;c;d;x;y;f(a,n)int*a;{for(i=x=y=d=0;n--;d=c)c="{HW^l>?X\x7f~"[a[i++]],x B&c),y B&d);*a=x;a[1]=y;}

Try it online!

Saved 2 bytes thanks to ceilingcat!!!

Inputs a pointer to an array of digits to display and its length (because C pointers carry no length info).
Return the number of lights turned on and the number turned off through the input pointer.


Vyxal, 41 bytes


Try it online or verify all test cases.


k-                                         # Push [-1, 1] (will be used later)
  »>xWƛo4ø₅=»                              # Push compressed integer 1098931065668123279355
             b                             # Convert to binary as a list
              7ẇ                           # Split into chunks of size 7
                :                          # Duplicate
                 ?h                        # Push the first digit of the input
                   i                       # Index it into the list
                    N                      # Negate: replace all 1s with -1
                     $                     # Swap so the list of size-7 chunks are at the top again
                      2ẋΠ                  # Cartesian product with self
                         v∩                # For each, transpose
                           vvƒ-            # For each inner pair, reduce by subtraction
                               ?S          # Push stringified input
                                 2l        # Get all overlapping pairs
                                   ⌊       # Convert them to integers
                                    İ      # Index these into the other list
                                     J     # Join the two lists together
                                      f    # Flatten
                                       vO  # Count the number of -1s and 1s in this list

Charcoal, 44 bytes

IE²L⭆⪪”←&⊞YJΣζ⁹)G;⧴4L⁴↷U³”¶Φθ‹⁼ι№λ§⁺ θξ⁼ι№λν

Try it online! Link is to verbose version of code. Explanation:

  ²                         Literal integer `2`
 E                          Map over implicit range
      ...                   Compressed string
     ⪪                      Split on
         ¶                  Literal newline
    ⭆                       Map over elements and join
           θ                Input string
          Φ                 Filtered where
              ι             Outer value
             ⁼              Equals
               №            Count of
                            Literal space
                  ⁺         Concatenated with
                    θ       Input string
                 §          Indexed by
                     ξ      Inner index
                λ           In current string piece
            ‹               Is less than
                       ι    Outer value
                      ⁼     Equals
                        №   Count of
                          ν Current input character
                         λ  In current string piece
   L                        Take the length
I                           Cast to string
                            Implicitly print

The compressed string splits to the list 1237, 14, 56, 017, 134579, 147, 2 which for each segment contains the digits (including the initial "off" state as a "digit") where the segment is off.


Perl 5, 96 bytes

tr/0-9/\167\3\136\37\53\75\175\7\177\77/;@_=($_,"\0$_");print unpack"%b*",$_[$_]&~$_[!$_]for 0,1

Try it online!


Input arrives in $_, pre-trimmed to just digits

Convert each digit into a pattern of bits corresponding to the segments that are turned on. The order of bits doesn't matter as long as they're consistent, so this order was chosen so that the whole list is represented with the fewest (octal) digits.

(tr modifies $_ in-place)


Make a copy with the starting blank included; keep both in the predefined @_ array

@_ = ($_, "\0$_");

(Now when we perform bitwise operations on these two values, it will apply the operation to each pair of consecutive bytes.)

Loop (twice) ...

for 0,1

... to perform the following forwards and backwards, so we get both the "turned on" and "turned off" counts. (Note: loop iterator is $_, which we no longer need it to hold the input.)

Mask each byte (of the transformed input string) with the "previous" byte, resulting in a string of bytes with bits set indicating change in one direction (which is why we do the whole thing twice):

$_[$_] & ~ $_[!$_]

(!$_ changes 0 to 1 and 1 to 0; (B & ~ A) gives you the bits that aren't set in A that are set in B)

Count the 1 bits from the expression above:

unpack "%b*",

Output the result:


Invoke Perl with the -l and -n options, for example, as:

perl -ln -e '«program here»' <<<'«input here»'

Each line of input is treated as a separate test case; after each test, the "turn on" and "turn off" counts are output on two separate lines.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.