RosettaCodeData/Task/Chinese-zodiac/JavaScript/chinese-zodiac.js

188 lines
4.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(() => {
"use strict";
// ---------- TRADITIONAL CALENDAR STRINGS -----------
// ats :: Array Int (String, String)
const ats = () =>
// 天干 tiangan 10 heavenly stems
zip(
chars("甲乙丙丁戊己庚辛壬癸")
)(
words("jiă yĭ bĭng dīng wù jĭ gēng xīn rén gŭi")
);
// ads :: Array Int (String, String)
const ads = () =>
// 地支 dizhi 12 terrestrial branches
zip(
chars("子丑寅卯辰巳午未申酉戌亥")
)(
words(
"zĭ chŏu yín măo chén sì " + (
"wŭ wèi shēn yŏu xū hài"
)
)
);
// aws :: Array Int (String, String, String)
const aws = () =>
// 五行 wuxing 5 elements
zip3(
chars("木火土金水")
)(
words("mù huǒ tǔ jīn shuǐ")
)(
words("wood fire earth metal water")
);
// axs :: Array Int (String, String, String)
const axs = () =>
// 十二生肖 shengxiao 12 symbolic animals
zip3(
chars("鼠牛虎兔龍蛇馬羊猴鸡狗豬")
)(
words(
"shǔ niú hǔ tù lóng shé " + (
"mǎ yáng hóu jī gǒu zhū"
)
)
)(
words(
"rat ox tiger rabbit dragon snake " + (
"horse goat monkey rooster dog pig"
)
)
);
// ays :: Array Int (String, String)
const ays = () =>
// 阴阳 yinyang
zip(
chars("阳阴")
)(
words("yáng yīn")
);
// --------------- TRADITIONAL CYCLES ----------------
const zodiac = y => {
const
iYear = y - 4,
iStem = iYear % 10,
iBranch = iYear % 12,
[hStem, pStem] = ats()[iStem],
[hBranch, pBranch] = ads()[iBranch],
[hElem, pElem, eElem] = aws()[quot(iStem)(2)],
[hAnimal, pAnimal, eAnimal] = axs()[iBranch],
[hYinyang, pYinyang] = ays()[iYear % 2];
return [
[
show(y), hStem + hBranch, hElem,
hAnimal, hYinyang
],
["", pStem + pBranch, pElem, pAnimal, pYinyang],
[
"", `${show((iYear % 60) + 1)}/60`,
eElem, eAnimal, ""
]
];
};
// ---------------------- TEST -----------------------
const main = () => [
1935, 1938, 1968, 1972, 1976, 1984,
new Date().getFullYear()
]
.map(showYear)
.join("\n\n");
// ------------------- FORMATTING --------------------
// fieldWidths :: [[Int]]
const fieldWidths = [
[6, 10, 7, 8, 3],
[6, 11, 8, 8, 4],
[6, 11, 8, 8, 4]
];
// showYear :: Int -> String
const showYear = y =>
zipWith(zip)(fieldWidths)(zodiac(y))
.map(
row => row.map(
([n, s]) => s.padEnd(n, " ")
)
.join("")
)
.join("\n");
// ---------------- GENERIC FUNCTIONS ----------------
// chars :: String -> [Char]
const chars = s => [...s];
// quot :: Integral a => a -> a -> a
const quot = n =>
m => Math.trunc(n / m);
// show :: Int -> a -> Indented String
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [
x[1], null, x[0]
] : x
);
// words :: String -> [String]
const words = s =>
// List of space-delimited sub-strings.
s.split(/\s+/u);
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs =>
// The paired members of xs and ys, up to
// the length of the shorter of the two lists.
ys => Array.from({
length: Math.min(xs.length, ys.length)
}, (_, i) => [xs[i], ys[i]]);
// zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
const zip3 = xs =>
ys => zs => xs.slice(
0,
Math.min(...[xs, ys, zs].map(x => x.length))
)
.map((x, i) => [x, ys[i], zs[i]]);
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
// A list constructed by zipping with a
// custom function, rather than with the
// default tuple constructor.
xs => ys => xs.map(
(x, i) => f(x)(ys[i])
).slice(
0, Math.min(xs.length, ys.length)
);
// MAIN ---
return main();
})();