Bowling

The rm2-template-bowling script uses the Perl-GD module (which uses the GD library) to create a form for keeping score in bowling games.

Right HandedLeft Handed
Bowling-rh.png
Bowling-rh-sm.png
Bowling-lh.png
Bowling-lh-sm.png

The templates I'm using on this page have 4 player rows in each group. If you need more or less player rows, you can use the -p option. For example, if you normally play in a group of five people, you could run the script as rm2-template-bowling -p 5 -o Bowling-5.png to generate a template with five player rows in each group.

I made this script after reading this comment on Reddit and thinking to myself, "you know, it wouldn't be that hard to make a template for bowling scores, and maybe somebody out there could use one" ... two hours later, here it is.

And for the record, I haven't bowled in over twenty years. This was just a fun little programming project for me, and if at least one person out there ends up using these templates, I consider it my good deed for the day.

License

This script is licensed under the MIT License.

The MIT License (MIT)

Copyright © 2023 John Simpson

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

The rm2-template-bowling Script

Download ⇒ rm2-template-bowling

#!/usr/bin/env perl -w
#
# rm2-template-bowling
# John Simpson <jms1@jms1.net> 2023-08-08
#
# Create reMarkable 2 templates for scoring bowling games
#
###############################################################################
#
# The MIT License (MIT)
#
# Copyright (C) 2023 John Simpson
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the “Software”),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
###############################################################################

require 5.005 ;
use strict ;
use warnings ;

use GD ;
use GD::Text::Align ;
use Getopt::Std ;

########################################
# reMarkable 2 screen attributes

my $rm2_page_w      = 1404 ;            # page width
my $rm2_page_h      = 1872 ;            # page height

########################################
# Margins and other attributes

my $menu_w          = 104 ;             # width of menu and close button areas
my $topbar_h        = 104 ;             # height of title area

my $gl_h            = 30 ;              # group - label row height
my $gp_h            = 90 ;              # group - player row height
my $padding         = 20 ;              # blank space around/between games

my $title           = 'Bowling Scores' ;
my $font_file       = "$ENV{'HOME'}/Library/Fonts/LiberationSans-Regular.ttf" ;
my $title_size      = 48 ;
my $label_size      = 12 ;

########################################
# Other globals

my %opt         = () ;                  # getopts()
my $lh          = 0 ;                   # -l    left handed (menu on right)
my $outfile     = 'Bowling.png' ;       # -o:   output filename
my $ppg         = 4 ;                   # -p:   players per game

my $white       = undef ;
my $grey25      = undef ;
my $grey50      = undef ;
my $grey75      = undef ;
my $black       = undef ;

###############################################################################
#
# Usage

sub usage(;$)
{
    my $msg = ( shift || '' ) ;

    print <<EOF ;
$0 [options]

Create a reMarkable 2 template for a simple monthly calendar.

-l or -r    Left- or Right-handed. Default is Right-handed.

-o ___      Specify the name of the file to write. Default is 'Bowling.png'.

-p ___      Players per game. Default is 4.

-h          Show this help message.

EOF

    if ( $msg ne '' )
    {
        print $msg ;
        exit 1 ;
    }

    exit 0 ;
}

###############################################################################
#
# Function to position a string around a point

sub place_text($$$$$$$$)
{
    my $img     = shift ;
    my $tx      = shift ;
    my $ty      = shift ;
    my $valign  = shift ;
    my $halign  = shift ;
    my $size    = shift ;
    my $text    = shift ;
    my $colour  = shift ;

    ########################################
    # Calculate real position for text

    my $a = GD::Text::Align->new( $img ,
        'valign' => $valign ,
        'halign' => $halign ,
    ) ;
    $a->set_font( $font_file , $size ) ;
    $a->set_text( $text ) ;

    my @b = $a->bounding_box( $tx , $ty , 0 ) ;

    ########################################
    # Add the text to the image
    # - (x,y) should be lower left corner

    $img->stringFT( $colour , $font_file , $size , 0 , $b[0] , $b[1] , $text ) ;
}

###############################################################################
#
# Add a row of labels

sub addrow_labels($$$$$)
{
    my $img     = shift ;
    my $rlx     = shift ;   # row left x
    my $rty     = shift ;   # top top y
    my $rrx     = shift ;   # row right x
    my $rby     = shift ;   # row bottom y

    ########################################
    # Calculate box sizes

    my $fw      = int( ( $rrx - $rlx ) / 13.5 ) ;   # frame width
    my $pw      = 2 * $fw ;                         # player width
    my $ty      = $rty + ( $rby - $rty ) / 2 ;      # text y

    ########################################
    # Player

    $img->setThickness( 1 ) ;
    $img->filledRectangle( $rlx , $rty , $rlx+$pw , $rby , $grey75 ) ;
    $img->rectangle( $rlx , $rty , $rlx+$pw , $rby , $black ) ;

    my $tx = $rlx + ( $pw / 2 ) ;
    place_text( $img , $tx , $ty , 'center' , 'center' , $label_size , 'Player' , $black ) ;

    ########################################
    # Frames

    for my $n ( 0 .. 9 )
    {
        my $flx = $rlx + $pw + $n * $fw ;
        my $frx = $flx + $fw ;

        $img->filledRectangle( $flx , $rty , $frx , $rby , $grey75 ) ;
        $img->rectangle( $flx , $rty , $frx , $rby , $black ) ;

        $tx = $flx + ( $fw / 2 ) ;
        place_text( $img , $tx , $ty , 'center' , 'center' , $label_size , $n+1 , $black ) ;
    }

    ########################################
    # Total

    my $tlx = $rlx + $pw + 10 * $fw ;

    $img->filledRectangle( $tlx , $rty , $rrx , $rby , $grey75 ) ;
    $img->rectangle( $tlx , $rty , $rrx , $rby , $black ) ;

    $tx = $tlx + ( $rrx - $tlx ) / 2 ;
    place_text( $img , $tx , $ty , 'center' , 'center' , $label_size , 'Total' , $black ) ;
}

###############################################################################
#
# Add a row of scoring boxes

sub addrow_scores($$$$$)
{
    my $img     = shift ;
    my $rlx     = shift ;   # row left x
    my $rty     = shift ;   # top top y
    my $rrx     = shift ;   # row right x
    my $rby     = shift ;   # row bottom y

    ########################################
    # Calculate box sizes

    my $fw      = int( ( $rrx - $rlx ) / 13.5 ) ;   # frame width
    my $pw      = 2 * $fw ;                         # player width
    my $sw      = int( $fw / 3 ) ;                  # sub-box width
    my $sh      = $sw ;                             # sub-box height

    ########################################
    # Player

    $img->setThickness( 1 ) ;
    $img->rectangle( $rlx , $rty , $rlx+$pw , $rby , $black ) ;

    ########################################
    # Frames

    for my $n ( 0 .. 9 )
    {
        ########################################
        # Outer box

        my $flx = $rlx + $pw + $n * $fw ;
        my $frx = $flx + $fw ;

        $img->rectangle( $flx , $rty , $frx , $rby , $black ) ;

        ########################################
        # Sub-boxes

        $img->line( $flx +   $sw , $rty , $flx +   $sw , $rty + $sh , $black ) ;
        $img->line( $flx + 2*$sw , $rty , $flx + 2*$sw , $rty + $sh , $black ) ;

        my $bx = $flx + ( ( $n < 9 ) ? $sw : 0 ) ;

        $img->line( $bx , $rty+$sh , $frx , $rty+$sh , $black ) ;
    }

    ########################################
    # Total

    my $tlx = $rlx + $pw + 10 * $fw ;

    $img->rectangle( $tlx , $rty , $rrx , $rby , $black ) ;
}

###############################################################################
###############################################################################
###############################################################################

getopts( 'hlro:p:' , \%opt ) ;
$opt{'h'} && usage() ;
$outfile = ( $opt{'o'} || $outfile ) ;
$ppg     = ( $opt{'p'} || 4 ) ;

########################################
# Left or right handed?

my $dal = $menu_w + $padding ;                  # drawing area left
my $dar = $rm2_page_w - $padding ;              # drawing area right
my $mal = 0 ;                                   # menu area left
my $mar = $menu_w ;                             # menu area right
my $msx = $mar ;                                # menu separator x
my $cal = $rm2_page_w - $menu_w ;               # close area left
my $car = $rm2_page_w - 1 ;                     # close area right
my $csx = $cal ;                                # close separator x

if ( $opt{'l'} )
{
    if ( $opt{'r'} )
    {
        usage( "ERROR: cannot use -l and -r together\n" ) ;
    }

    $dal = $padding ;                           # drawing area left
    $dar = $rm2_page_w - $menu_w - $padding ;   # drawing area right
    $mal = $rm2_page_w - $menu_w ;              # menu area left
    $mar = $rm2_page_w - 1 ;                    # menu area right
    $msx = $mal ;                               # menu separator x
    $cal = 0 ;                                  # close area left
    $car = $menu_w ;                            # close area right
    $csx = $car ;                               # close separator x
}

###############################################################################
#
# Start the image

my $img = new GD::Image( $rm2_page_w , $rm2_page_h ) ;

########################################
# Allocate colours - first colour is used as background

$white  = $img->colorAllocate( 255 , 255 , 255 ) ;
$grey75 = $img->colorAllocate( 192 , 192 , 192 ) ;
$grey50 = $img->colorAllocate( 128 , 128 , 128 ) ;
$grey25 = $img->colorAllocate(  64 ,  64 ,  64 ) ;
$black  = $img->colorAllocate(   0 ,   0 ,   0 ) ;

############################################################
# Menu/title zones

$img->setThickness( 1 ) ;

########################################
# Grey area under close button, top right/left
# and line across top of page, bottom of title area

$img->filledRectangle( $cal , 0 , $car , $topbar_h-1 , $grey75 ) ;
$img->line( 0 , $topbar_h-1 , $rm2_page_w-1 , $topbar_h-1 , $black ) ;
$img->line( $csx , 0 , $csx , $topbar_h-1 , $black ) ;

########################################
# Grey area under menu, on left/right, drawn *after* the line across
# the top of page so it covers whichever end is appropriate

$img->filledRectangle( $mal , 0 , $mar , $rm2_page_h-1 , $grey75 ) ;
$img->line( $msx , 0 , $msx , $rm2_page_h-1 , $black ) ;

########################################
# Add title

my $tx = int( $rm2_page_w / 2 ) ;
my $ty = int( $topbar_h   / 2 ) ;
place_text( $img , $tx , $ty , 'center' , 'center' , $title_size , $title , $black ) ;

###############################################################################
#
# Add groups of rows

my $y = $topbar_h + $padding ;

while ( ( $y + $gl_h + $ppg * $gp_h ) < ( $rm2_page_h - $padding ) )
{
    ########################################
    # Add the group's row of labels

    addrow_labels( $img , $dal , $y , $dar , $y + $gl_h ) ;
    $y += $gl_h ;

    ########################################
    # Add a row of score boxes for each player in the group

    for my $n ( 1 .. $ppg )
    {
        addrow_scores( $img , $dal , $y , $dar , $y+$gp_h ) ;
        $y += $gp_h ;
    }

    ########################################
    # Padding between groups

    $y += $padding ;
}

###############################################################################
#
# Write the output file

open( O , '>' , $outfile )
    or die "ERROR: can't create \"$outfile\": $!\n" ;
binmode O ;
print O $img->png() ;
close O ;

Generated 2024-11-07 13:40:42 +0000
initial-86-g8ae27cf 2024-11-07 13:40:26 +0000