Wherein the sky and earth reach out to each other to meet in the middle, where we live out our lives in the balance of energies.
THE WEEKLY CHALLENGE – PERL & RAKU #103
episode one:
“Earthly Branches and Heavenly Stems”
Task 1
Chinese Zodiac
Submitted by: Mohammad S Anwar
You are given a year $year
.
Write a script to determine the Chinese Zodiac
for the given year $year
. Please check out wikipage for more information about it.
- The animal cycle: Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Goat, Monkey, Rooster, Dog, Pig.
- The element cycle: Wood, Fire, Earth, Metal, Water.
Example 1:
Input: 2017
Output: Fire Rooster
Example 2:
Input: 1938
Output: Earth Tiger
Method
The Chinese Calendar system is based around a sexagenary year cycle, or 60 years. Within that cycle each year is denoted by a specific combination of one of twelve animals, five elements and two energies. Because of the twelve animal signs it is commonly referred to as a “zodiac”, but there the correspondence ends. One might consider a link through the 12-month lunar cycle, but the Chinese system is in fact an approximation of the 12-year Jovian cycle and the numeric resemblance is better regarded as coincidence, or perhaps better labeled synchronicity.
Twelve is a kind of a go-to number in all things cosmological and astronomical, and the patterning of months into years and beyond certainly falls into that territory. More connections can be made through the Babylonian sexagenary numeral system, and of note the Babylonians originated the 30° celestial sectioning that later evolved into the western zodiac. This repeated confluence of tens and twelves across the world is quite remarkable.
We’ll stay clear of the metaphysical attributions of these three categorizations as being well outside our purview, and simply list that there are twelve animals repeated one per year in sequence, five elements that cycle in two-year intervals, and two principle energies, “yin” and “yang”, that define any given year.
So we have cycles of length 12, 2 and either 5 or 10 depending on how you look at it. Cycling number sequences take the mind immediately to modulo mathematics and so that’s what we used.
The basic strategy was to create arrays of each of the aspects, then access these arrays by index, computed by applying modulo division to the year. Either the date or the array is adjusted to correct any alignment issues.
It’s interesting to note that the aspects requested, the element and the animal sign, do not alone uniquely define a given year in the sexagenary cycle. To assign the five elements across the ten “heavenly stems” each element is repeated across two years. A further qualifier is made, that of a guiding energy, either “yin” or “yang”. Together these aspects make up a attribute known as the “heavenly stem”, of 10 variations.
PERL 5 SOLUTION
For my own solution I broke the problem down systematically and methodically, first creating a function to turn a Gregorian year into a point on the sexagenary cycle, then making a trio of functions to convert that cycle index into the correct animal, element and energy aspects. By taking the additional step to make the sexagenary conversion, it was relatively straightforward to accommodate dates before the common era, parsing out the era indicators with a regular expression. And more so, once you’re in there looking for “BC”, it’s not much more work to generalize the search into a variety of other conventions, such as BCE, or adding periods between the initials.
2021 : Yin Metal Ox
Here’s the code:
use warnings;
use strict;
use feature ":5.26";
use feature qw(signatures);
no warnings 'experimental::signatures';
our @elements = qw( Wood Fire Earth Metal Water );
our @animals = qw( Rat Ox Tiger
Rabbit Dragon Snake
Horse Goat Monkey
Rooster Dog Pig) ;
our @energy = qw( Yin Yang );
say zodiac($_) for @ARGV;
sub zodiac($year) {
my $sy = sexagenary_year($year);
my $animal = animal($sy);
my $element = element($sy);
my $energy = energy($sy);
my $out = "$year : $energy $element $animal";
return $out; ## testing
}
sub sexagenary_year( $year, $era = '' ) {
## Gregorian year --> Sexagenary year
## divided into AD/A.D./CE/C.E. and BC/B.C./C/BCE/B.C.E.
## no mark is of course assumed to be CE
## SY 60 is returned as 0.
($year, $era) = $year =~ /^(\d+)\s?([ABC]?)/i;
if ( not $era or $era eq 'A' or $era eq 'C' ) {
return 58 if $year == 1;
return 59 if $year == 2;
return 0 if $year == 3;
return ($year - 3) % 60;
}
else {
return 60 - abs($year+2) % 60;
}
}
sub animal( $sexy_year ) {
## 12-year cycle, 1-based
## SY goes 1-59, 0
my $idx = ($sexy_year-1) % 12 ;
return $animals[ $idx ];
}
sub element( $sexy_year ) {
## 10-year cycle of 5 x 2-year periods
my $idx = int ( ($sexy_year-1) % 10 ) / 2;
return $elements[$idx];
}
sub energy( $sexy_year ) {
## alternating yin and yang repetition
my $idx = $sexy_year % 2;
return $energy[$idx];
}
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