Peter
Peter Campbell Smith

Product lines

Weekly challenge 267 — 29 April 2024

Week 267 - 29 Apr 2024

Task 2

Task — Line counts

You are given a string, $str, and a 26-items array @widths containing the width of each character from a to z. Write a script to find out the number of lines and the width of the last line needed to display the given string, assuming you can only fit 100 width units on a line.

Examples


Example 1
Input: $str = "abcdefghijklmnopqrstuvwxyz"
       @widths = (10,10,10,10,10,10,10,10,10,10,10,10,10,
	          10,10,10,10,10,10,10,10,10,10,10,10,10)
Output: (3, 60)

Line 1: abcdefghij (100 pixels)
Line 2: klmnopqrst (100 pixels)
Line 3: uvwxyz (60 pixels)

Example 2
Input: $str = "bbbcccdddaaa"
       @widths = (4,10,10,10,10,10,10,10,10,10,10,10,10,
	         10,10,10,10,10,10,10,10,10,10,10,10,10)
Output: (2, 4)

Line 1: bbbcccdddaa (98 pixels)
Line 2: a (4 pixels)

Analysis

I can't think of a clever way to do this so have just done the obvious: take one character a time from $str, see if it will fit on the current line, if so add it to that line or if not, start a new line.

It helps to first create a hash %pixels such that $pixels{$ch} is the width of $ch.

In the 1980s I did this for real as we had a daisy-wheel printer with characters of varying widths. It could 'print' spaces of any number of points width so that text could be right-and-left justified. Also, it could print left-to-right or right-to-left, and I fed it alternate lines in forward and reverse order to minimise the time the print head spent moving horizontally between lines.

And then laser printers and Microsoft Word came along and all that was redundant - until today.

Try it 

Try running the script with any input:



example: califragilisticexpialidocious



example: 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
If you don't supply 26 values the rest will be set to 10

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2024-04-29
use utf8;     # Week 267 - task 2 - Line counts
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';

line_counts('abcdefghijklmnopqrstuvwxyz', 
    [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 
     10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]);
line_counts('bbbcccdddaaa', 
    [ 4, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 
     10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]);
line_counts('thequickbrownfoxjumpsoverthelazydog', 
    [15, 27, 12, 28, 16, 29, 33, 21, 14, 29, 36, 13, 14, 
     9, 18, 23, 22, 35, 27, 15, 25, 21, 19, 35, 23, 15]);

sub line_counts {
    
    my ($str, @widths, $ch, $limit, %pixels, $lines, 
        $chars, $j, $line, @lines, @line_length);
    
    $str = $_[0];
    @widths = @{$_[1]};
    $limit = 100;
    
    # set $pixels{$char} to the width of $char
    for $j (0 .. 25) {
        $pixels{chr(ord('a') + $j)} = $widths[$j];
    }
    
    # loop over characters in $str
    $line = 0;
    $chars = $limit;
    while ($str =~ m|([a-z])|g) {
        
        # needs a new line
        $ch = $1;
        if ($chars + $pixels{$ch} > $limit) {
            $line ++;
            $chars = $pixels{$ch};
            
        # fits on current line
        } else {
            $chars += $pixels{$ch};
        }
        
        # save for explanation
        $lines[$line] .= $ch;
        $line_length[$line] += $pixels{$ch};
    }
    
    # results
    say qq[\nInput:  \@str = '$str'];
    say qq[        \$widths = (] . join(', ', @widths[0 .. 12]) . ',';
    say qq[                   ] . join(', ', @widths[13 .. 25]) . ')';
    say qq[Output: ($line, $chars)];
    for $j (1 .. @lines - 1) {
        say qq[        Line $j: $lines[$j] ($line_length[$j] pixels)];
    }
    
}

Output


Input:  @str = 'abcdefghijklmnopqrstuvwxyz'
        $widths = (10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
                   10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
                   10, 10, 10, 10, 10, 10)
Output: (3, 60)
        Line 1: abcdefghij (100 pixels)
        Line 2: klmnopqrst (100 pixels)
        Line 3: uvwxyz (60 pixels)

Input:  @str = 'bbbcccdddaaa'
        $widths = (4, 10, 10, 10, 10, 10, 10, 10, 10, 10,
                  10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
                  10, 10, 10, 10, 10, 10)
Output: (2, 4)
        Line 1: bbbcccdddaa (98 pixels)
        Line 2: a (4 pixels)

Input:  @str = 'thequickbrownfoxjumpsoverthelazydog'
        $widths = (15, 27, 12, 28, 16, 29, 33, 21, 14, 29,
                   36, 13, 14, 9, 18, 23, 22, 35, 27, 15,
                   25, 21, 19, 35, 23, 15)
Output: (9, 33)
        Line 1: thequ (99 pixels)
        Line 2: ickb (89 pixels)
        Line 3: rown (81 pixels)
        Line 4: fox (82 pixels)
        Line 5: jump (91 pixels)
        Line 6: sove (82 pixels)
        Line 7: rthel (100 pixels)
        Line 8: azydo (99 pixels)
        Line 9: g (33 pixels)