#!/usr/bin/perl use strict; use warnings; use utf8; #add my user perl library path, this is particular to each person's setup #use lib qw( /usr/pkg/lib/perl5/5.24.0 /meta/p/peteyboy/perl5/lib/perl5 ); #make request for all of the following use CGI::Tiny; use Mojo::Template; use Mojo::Loader 'data_section'; use Readonly; #use List::Util qw(first); use URI; use experimental 'smartmatch'; #=== # MAKE SURE ORIGIN PAGE MATCHES FILENAME (especially if you are testing changes)! # #=== my $ORIGIN_PAGE = "/cgi-bin/wordle-life.cgi"; my $defaulttext = "Paste your wordle share here (replace this text)"; my $PATH_PARAM = "path_info"; my $WORDLE_INPUT_PARAM = "wordle-result"; my $SHARENOTE_PARAM = "share-note"; cgi { #error handler from the CGI:Tiny cookbook my $cgi = $_; $cgi->set_error_handler(sub { my ($cgi, $error, $rendered) = @_; warn $error; unless ($rendered) { if ($cgi->response_status_code == 413) { $cgi->render(json => {error => 'Request body limit exceeded'}); }elsif ($cgi->response_status_code == 400) { $cgi->render(json => {error => 'Bad request'}); } else { $cgi->render(json => {error => 'Internal server error'}); } } }); my $wordle; my $method = $cgi->method; my $rle = ""; my $isshare = 0; #flag for sharing, default false my $sharenote = ""; #a note someone can tack onto their share my $navigate = 0; #flag to go to different page, default false #Simple switch: make a one page app, and always go back to the same page. #GET or HEAD loads the page blank, POST runs the Life file maker if ($method eq 'HEAD') { $wordle = $defaulttext; } elsif ($method eq 'GET') { #Check path_info to see if we are navigating (ignoring sharing query params if there?) #if (first { $_ eq $PATH_PARAM } @{$cgi->query_param_names} ){ if ( $PATH_PARAM ~~ @{$cgi->query_param_names} ){ $navigate = mock_path_info($cgi->query_param($PATH_PARAM)); #true and populated } #if there are sharing query parameters put them in $wordle and activate share page logic elsif ($WORDLE_INPUT_PARAM ~~ @{$cgi->query_param_names} ){ $wordle = $cgi->query_param($WORDLE_INPUT_PARAM); $isshare = 1; #true $sharenote = $cgi->query_param($SHARENOTE_PARAM); }else{ $wordle = $defaulttext; } } elsif ($method eq 'POST') { #textarea name, pull the value from POST and reload into textarea $wordle = $cgi->body_param($WORDLE_INPUT_PARAM); #if input textarea not changed from default text, do nothing unless ($wordle =~ /\Q$defaulttext\E/ or $wordle eq '') { #[Cc] /){ #cheat, if they put in something that looks like an RLE, pass output through if ($wordle =~ /^#[Cc] /){ $rle = $wordle; #if none of that, do the thing: generate RLE }else{ $rle = generate_rle($wordle); } } #$output = $mt->render($template, { WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, sharenote => $sharenote }); } else { #some other request? PUT, DELETE? $cgi->set_response_status(405)->render; exit; } #die "Invalid wordle parameter" unless length $wordle; #Load template from DATA section and output #my $mt = Mojo::Template->new(auto_escape => 1, vars => 1); my $mt = Mojo::Template->new(vars => 1); my $template; my $output; #TODO: this is ugly; clean up if ($method eq 'GET' && $navigate ) { my $template_name = template_from_path_info ($navigate); #TODO: Check against a list of valid templates! #if ( grep ( /^$template_name$/, ('index.html.ep', 'about.html.ep') )) { if ( $template_name ~~ ['index.html.ep', 'about.html.ep'] ) { $template = data_section __PACKAGE__, $template_name; $output = $mt->render($template, { ORIGIN_PAGE => $ORIGIN_PAGE }); #this isn't general purpose, what template needs what variables? }else{ #TODO: fix so as not to need copied code from the else below? $template = data_section __PACKAGE__, 'index.html.ep'; $output = $mt->render($template, { WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote }); } }else{ $template = data_section __PACKAGE__, 'index.html.ep'; $output = $mt->render($template, { WORDLE_INPUT_PARAM => $WORDLE_INPUT_PARAM, ORIGIN_PAGE => $ORIGIN_PAGE, defaulttext => $defaulttext, wordle => $wordle, rle => $rle, isshare => $isshare, sharenote => $sharenote }); } $cgi->render(html => $output ); }; #Try to Generate the RLE. Makes a lot of assumptions sub generate_rle (\$) { my $headline = ""; my $rleline = ""; my $rowcount; my $myline; Readonly my $headline_prefix => "#C "; #Part of RLE spec https://conwaylife.com/wiki/Run_Length_Encoded Readonly my $row_prefix => "x = 5"; #for wordle always 5 Readonly my $col_prefix => ",y = "; Readonly my $lifeviewer_settings => "\n[[\n GPS 3\n ZOOM 23\n COLOR ALIVE LIME\n COLOR BACKGROUND MIDNIGHTBLUE\n STOP 100\n GRID\n]]"; #Part of Viewer Spec #Readonly my $lifeviewer_settings => "\n[[\n GPS 3\n ZOOM 23\n THEME INVERSE\n STOP 100\n GRID\n]]"; #Part of Viewer Spec my $wordle = shift; # this is how you get variables in your function call! $rowcount = 0; my @lines = split /^/, $wordle; foreach $myline (@lines) { chomp($myline); if ($headline eq "" && $myline =~ /Wordle/){ $headline = $headline_prefix . $myline; }else{ #remove all line endings with magic perl identifier R $myline =~ s/\R//; $myline =~ s/^(.*)$/$1\$/; #these replaces are logicless, so they need to be done in this order! #in case of text mode (Discord) $myline =~ s/:yellow_square:/o/g; $myline =~ s/:green_square:/o/g; $myline =~ s/:blue_square:/o/g; #HC equiv yellow $myline =~ s/:orange_square:/o/g; #HC equiv green $myline =~ s/:red_square:/o/g; #wordum red fails $myline =~ s/:[a-z_]*square:/b/g; # for normal unicode $myline =~ s/\N{U+1f7e8}/o/g; #yellow hit $myline =~ s/\N{U+1f7e9}/o/g; #green hit $myline =~ s/\N{U+1F7E6}/o/g; #blue $myline =~ s/\N{U+1F7E7}/o/g; #orange $myline =~ s/\N{U+1F7E5}/o/g; #red $myline =~ s/\N{U+2B1B}/b/g; #black $myline =~ s/\N{U+2B1C}/b/g; #white #broken copy/paste text $myline =~ s/black_large_square/b/g; $myline =~ s/white_large_square/b/g; $myline =~ s/yellow_square/o/g; $myline =~ s/green_square/o/g; $myline =~ s/blue_square/o/g; $myline =~ s/orange_square/o/g; $myline =~ s/red_square/o/g; #wordum red fails #skip blank lines, count rows added if(length($myline) > 1){ $rowcount += 1; $rleline= $rleline . $myline; } } } #this is the format with b,o, and $. Done lazily because I don't compress the repeated b or o. $rleline =~ s/\$$/\!/; #Correct termination of last line of RLE is !, not $, so replace it. return "$headline\n$row_prefix$col_prefix$rowcount\n$rleline\n$lifeviewer_settings"; } #Because SDF's nginx config is broken, we have to use query params to *navigate* sub mock_path_info { my $uri = URI->new (shift); return $uri->path; } sub template_from_path_info { my $template=''; my $path = shift; if ($path =~ /^\//){ $template = $path; $template =~ s/^.//; $template =~ tr/\//./; $template = $template . ".html.ep"; } return $template; } __DATA__ @@ index.html.ep
about
% if ($isshare) {
They said:<%= $sharenote %> Skip to step 2 of the instructions below and see the shared wordle-life happen! Play your Wordle Share score in Conway's Life! Hey, someone wants to share their Wordle->Life score with you!
%if ($sharenote){
Hey, nerds, isn't it funny how the Wordle Scores can look like a glider in that old computer programming exercise, Conway's Life?
Paste your wordle share below and submit to convert it to a Conway's Life file so you can run it (to learn more about Conway's Life, read this).
Click "Submit" and an RLE format file made for Conway's Game of Life will appear in the box below and get loaded into the Life Viewer.
Watch your Wordle score play LIFE!
If the RLE is correct, it should be loaded in the Life Viewer, and so you are ready to go!
For best results with the Life Viewer, you may adjust the default Zoom and the playback speed, then press the Play button:
Press "[Pause]" when it seems stable.
This should work with the share/copy button in wordle, or copy/pasting your friends' wordles out of Discord or wherever as well. See whose Wordle Score runs the coolest!
% if ($rle ne ""){
Share your wordle-life! You can copy/paste the RLE into your favorite social media post or email OR Click this button to go to a share page where you can copy/paste the url to someone
If it doesn't seem to be working:
If there is extra text or lines in your paste, you might not get a valid file in the box above, so look and make sure it looks like a proper RLE file, for example:
#C Wordle 235 3/6 x = 5,y = 3 boobo$bobbo$ooooo! [[ GPS 3 ZOOM 23 COLOR ALIVE LIME COLOR BACKGROUND BLUE STOP 100 GRID ]]
(the main thing that needs to be right are the first 3 lines. If the 3rd line has any colon : symbols, just try deleting them. The stuff between double brackets aren't part of the RLE but are comments that set some defaults for the Life Viewer)
For details about the Life RLE file format above, read this.
@@ about.html.ep
This is an about page.
Click >here to go Home
Click >here to stay put
Thanks to hankchizljaw for making his basic css publicly available
Made in February 2022