18
\$\begingroup\$

The Bible is one of the most influential books ever written, and commonly cited as the best selling book of all time. It was written by approximately 40 different authors over hundreds of years before being compiled into it's current form. But what's interesting about The Bible is the way it's divided up. It is split up into 2 different testaments, which are split up into 66 smaller books, which are each split up into smaller chapters, which are each split up into individual verses.

I thought it would be a fun challenge to try to encode the number of chapters in each book in the shortest code possible. So for today's challenge, you must write a program or function that takes one of the books as input, and outputs the number of chapters in that book according to The King James Version.

You may take IO in any reasonable format, for example reading/writing STDIN/STDOUT or a file, function arguments/return values, prompting the user, etc. are all allowed. The input will always be one of the 66 books of The Bible, and only lowercase. This means that if you are given any other input, undefined behavior is allowed. Since there are only 66 possible inputs and outputs, they are all provided here, according to Wikipedia's page on Bible chapters in The King James Version:

genesis         50
exodus          40
leviticus       27
numbers         36
deuteronomy     34
joshua          24
judges          21
ruth            4
1 samuel        31
2 samuel        24
1 kings         22
2 kings         25
1 chronicles    29
2 chronicles    36
ezra            10
nehemiah        13
esther          10
job             42
psalms          150
proverbs        31
ecclesiastes    12
song of solomon 8
isaiah          66
jeremiah        52
lamentations    5
ezekiel         48
daniel          12
hosea           14
joel            3
amos            9
obadiah         1
jonah           4
micah           7
nahum           3
habakkuk        3
zephaniah       3
haggai          2
zechariah       14
malachi         4
matthew         28
mark            16
luke            24
john            21
acts            28
romans          16
1 corinthians   16
2 corinthians   13
galatians       6
ephesians       6
philippians     4
colossians      4
1 thessalonians 5
2 thessalonians 3
1 timothy       6
2 timothy       4
titus           3
philemon        1
hebrews         13
james           5
1 peter         5
2 peter         3
1 john          5
2 john          1
3 john          1
jude            1
revelation      22

Since this challenge is about finding the optimal way to encode every book name and chapter count, using any builtins that give information about The Bible are not permitted. However, since it would be interesting to see which languages have such builtins, feel free to share a second non-competing version along with your answer. Fetching information from external sources is also not permitted (none of the standard loopholes are allowed, but I thought it would be useful to explicitly mention that one).

As usual, this is a challenge, so try to make the shortest possible program (measured in bytes) as you can. Have fun golfing!

\$\endgroup\$
  • 3
    \$\begingroup\$ The output is not fixed, so why is this kolmogorov-complexity? \$\endgroup\$ – LyricLy Oct 2 '17 at 23:23
  • 3
    \$\begingroup\$ @LyricLy Because even though the output is not fixed, it is still about seeing the most efficient way to generate two different sets of data: 1) The names of the books (or a portion of the name long enough to recognize it) and 2) The corresponding number of chapters. \$\endgroup\$ – DJMcMayhem Oct 2 '17 at 23:25
  • 3
    \$\begingroup\$ you just see all the ones with a normal amount and then just psalms which is like HALF THE BIBLE \$\endgroup\$ – HyperNeutrino Oct 2 '17 at 23:35
  • 4
    \$\begingroup\$ I wanna enklact my way out of this challenge but I can't... \$\endgroup\$ – totallyhuman Oct 3 '17 at 1:32
  • 3
    \$\begingroup\$ @DJMcMayhem You've forgotten the fact that there are not 66 unique letters. :P \$\endgroup\$ – totallyhuman Oct 3 '17 at 1:38
12
\$\begingroup\$

Jelly, 127 bytes

“£ÐgƁ÷ḅ*Wßßɦ*⁷ċṗṿỵ×Ɓṿ⁷ḢQ’b7+\;“BƝ‘
OḄ;407;270;“ọḷḊḲɦ‘%/i@“¡Ṙ×!⁸Ọ5`ỊV-ṙQȷṖÞ(sĿȮ^k³M"ɓmEf¤*0C[ạƇŻȥ#BṚñİmZẓeȦUƈ!ċ€@Ȧʋ)ƙḅyẉ’b158¤ị¢

Try it online!

Test Cases

How it Works

Essentially, this tries to convert the ords of the characters input into a binary value, for example "joel" -> [106, 111, 101, 108]-> 2^3*106 + 2^2*111 + 2^1*101 + 2^0*108.

Then, this value is taken mod 407, then mod 270, then [a few more mods], then mod 160. This is useful because it maps all 66 string inputs to integers between 0 and 158 (lucky on final mod).

The integer is indexed from the integer list “ọḷḊḲɦ...ƙḅyẉ’b158¤ to find the value of n such that the input has the n-th least number of chapters. Joel happens to have the 7-th least number of chapters.

This value of n is further indexed into the list “£ÐgƁ÷ḅ*Wßßɦ*⁷ċṗṿỵ×Ɓṿ⁷ḢQ’b7+\;“BƝ‘ to find the exact number of chapters.

Possible improvement: inputs with the same number of chapters can hash to the same value from the mods (0% collision is not necessary), but I did not account for that in my program to determine the sequence of mods.

\$\endgroup\$
  • \$\begingroup\$ 126 bytes \$\endgroup\$ – caird coinheringaahing Oct 3 '17 at 6:37
  • \$\begingroup\$ Save a byte by avoiding @ (%/i@“...’b158¤ị¢ -> %/“...’b158¤iị¢). Save another byte by using an offset of 160 to a single code-page index list at the start (OḄ;407;270;“ọḷḊḲɦ‘ -> OḄ;“ẋn=:!ס‘+160¤). \$\endgroup\$ – Jonathan Allan Oct 4 '17 at 21:59
10
\$\begingroup\$

Excel, 373 bytes

Reusing @Misha's approach from the Mathematica answer (6a+b+8c+5d+3e modulo 151):

=CHOOSE(MOD(6*CODE(LEFT(A1,1))+CODE(MID(A1,2,1))+8*CODE(MID(A1,3,1))+5*CODE(MID(A1&A1,4,1))+3*CODE(MID(A1&A1,5,1)),151)+1,30,,,,27,,,,3,12,,,9,,149,,,28,,,13,,,35,3,,35,7,,8,,20,,49,23,13,4,,,4,,,,,,,2,,,39,,,15,3,,3,9,,12,27,,,,,,51,15,,,,70,,65,,21,11,,,4,,24,,,6,,2,1,,,,,,2,,,30,,,,,,23,,,33,,20,,,15,,,4,,4,,,12,2,,2,47,23,,2,,,,,41,,,3,,,5,,,,,11,,21,5,,,,5,2,3,26)+1

Lookup returns chapters -1, and then add the one. This changes 10 into 9 twice, and ,1, into ,, 4 times.

Updated to old approach. Excel, 460 401 bytes

Save as CSV, Book Name entered at end of first line (C1), result displayed in C2:

1c,16,revelation
1co,29,"=VLOOKUP(LEFT(C1,1)&MID(C1,3,1)&MID(C1,6,1),A:B,2)"
1j,5
1k,22
1p,5
1s,31
1t,6
1ts,5
2c,13
2co,36
2j,1
2k,25
2p,3
2s,24
2t,4
2ts,3
3j,1
a,9
at,28
c,4
d,12
du,34
e,12
ee,48
eh,6
eo,40
er,10
g,6
gn,50
h,3
hbw,13
hg,2
hs,14
i,66
j,42
jd,1
jds,21
je,3
jh,21
jm,5
jn,4
jr,52
js,24
lm,5
lv,27
m,7
ml,4
mr,16
mt,28
n,3
nhi,13
nm,36
o,1
p,150
pi,1
pip,4
po,31
r,16
rt,4
rv,22
s,8
t,3
zc,14
zp,3

For the lookup table, we can leave out et 10 and l 24 because these match on er 10 and js 24 respectively.

\$\endgroup\$
  • \$\begingroup\$ We don't see excel formula answers very often that require a lookup table. Upvote for the novelty and the execution. You might want to search/post some meta questions about scoring in excel. Specifically, I think you might owe a couple more bytes for the layout of the A column. Also, you probably want to move A to C and B:C to A:B so you don't get "charged" for all the empty cells in A. Off the top of my head, I think the size of the TSV/CSV text file for the whole sheet might be the right score? \$\endgroup\$ – Sparr Oct 3 '17 at 18:02
  • \$\begingroup\$ Generally, I think that the bytecount of the characters in the .csv file (in this case minus the bytecount of the included input value) should be the score of these types of Excel answer - which by my count leaves the above at a score of 401 bytes \$\endgroup\$ – Taylor Scott Oct 4 '17 at 14:11
  • \$\begingroup\$ @TaylorScott, I had CR-LF as new line characters. Assume the 401 v. 464 is because of this? \$\endgroup\$ – Wernisch Oct 4 '17 at 14:56
  • \$\begingroup\$ @Wernisch, in short, yes - you can assume that the linefeeds are stored as the ASCII character literal with charcode 10, meaning that they count as 1 byte \$\endgroup\$ – Taylor Scott Oct 4 '17 at 14:59
9
\$\begingroup\$

JavaScript (ES6), 251 197 bytes

s=>`-  cE1$ " +%&  %  &!!· $!#&!!)!'=6 &!6".!  -!!Q$"/  779@@= % & $'1 U( I>!!  K * "S< :  9$!C %  . $. 9E1/ %!!'" + $ % `.split`!`.join`   `.charCodeAt(parseInt(s[2]+s[0]+s[5],36)%913%168%147)-33

Test

let f =

s=>`-  cE1$ " +%&  %  &!!· $!#&!!)!'=6 &!6".!  -!!Q$"/  779@@= % & $'1 U( I>!!  K * "S< :  9$!C %  . $. 9E1/ %!!'" + $ % `.split`!`.join`   `.charCodeAt(parseInt(s[2]+s[0]+s[5],36)%913%168%147)-33

o.innerText = [
  ["genesis",         50 ],
  ["exodus",          40 ],
  ["leviticus",       27 ],
  ["numbers",         36 ],
  ["deuteronomy",     34 ],
  ["joshua",          24 ],
  ["judges",          21 ],
  ["ruth",            4  ],
  ["1 samuel",        31 ],
  ["2 samuel",        24 ],
  ["1 kings",         22 ],
  ["2 kings",         25 ],
  ["1 chronicles",    29 ],
  ["2 chronicles",    36 ],
  ["ezra",            10 ],
  ["nehemiah",        13 ],
  ["esther",          10 ],
  ["job",             42 ],
  ["psalms",          150],
  ["proverbs",        31 ],
  ["ecclesiastes",    12 ],
  ["song of solomon", 8  ],
  ["isaiah",          66 ],
  ["jeremiah",        52 ],
  ["lamentations",    5  ],
  ["ezekiel",         48 ],
  ["daniel",          12 ],
  ["hosea",           14 ],
  ["joel",            3  ],
  ["amos",            9  ],
  ["obadiah",         1  ],
  ["jonah",           4  ],
  ["micah",           7  ],
  ["nahum",           3  ],
  ["habakkuk",        3  ],
  ["zephaniah",       3  ],
  ["haggai",          2  ],
  ["zechariah",       14 ],
  ["malachi",         4  ],
  ["matthew",         28 ],
  ["mark",            16 ],
  ["luke",            24 ],
  ["john",            21 ],
  ["acts",            28 ],
  ["romans",          16 ],
  ["1 corinthians",   16 ],
  ["2 corinthians",   13 ],
  ["galatians",       6  ],
  ["ephesians",       6  ],
  ["philippians",     4  ],
  ["colossians",      4  ],
  ["1 thessalonians", 5  ],
  ["2 thessalonians", 3  ],
  ["1 timothy",       6  ],
  ["2 timothy",       4  ],
  ["titus",           3  ],
  ["philemon",        1  ],
  ["hebrews",         13 ],
  ["james",           5  ],
  ["1 peter",         5  ],
  ["2 peter",         3  ],
  ["1 john",          5  ],
  ["2 john",          1  ],
  ["3 john",          1  ],
  ["jude",            1  ],
  ["revelation",      22 ]
]
.every(s => (log += s[0] + ' --> ' +  (res = f(s[0])) + '\n', res == s[1]), log = '')
? log + '\nSUCCESS' : log + '\nFAIL'
<pre id=o></pre>

Formatted and commented

s =>                                 // s = book name
  `-  cE1$ " +%&  %  &!!· (...)`     // data string (truncated)
  .split`!`.join`   `                // inflate padding characters '!' -> 3 spaces
  .charCodeAt(                       // read the ASCII code at the position found by
    parseInt(s[2] + s[0] + s[5], 36) // parsing 3 characters of the input in this exact
    % 913 % 168 % 147                // order as base 36 and applying three consecutive
  ) - 33                             // modulo operations, then subtract 33
\$\endgroup\$
  • 2
    \$\begingroup\$ You are officially the master of finding the most efficient hashing method in JS... Or do you have a program which finds methods for you? ;) \$\endgroup\$ – ETHproductions Oct 3 '17 at 21:27
  • \$\begingroup\$ @ETHproductions I did write a generic Node program at some point, but it's not especially fast so I end up writing custom code most of the time... O_o \$\endgroup\$ – Arnauld Oct 3 '17 at 21:40
9
\$\begingroup\$

Mathematica: 323 294 bytes

Uncompress["1:eJxTTMoPSpvOwMBQzAIkfDKLSzJlgAwCBEhtJi8qwQUnpqESsqgEHyqhAjePBc7lgBOccEIUThiBCAm4AayECUZUghmV0EAlBFAdxILqN17CgWMCNwUn4QQnxEAEDyqBcLgkKsGO6gUmLAROX8rjJSRQCSU4IYpKILzAiDfEebG4wADVDmZchBYqgRYVbLgIRPiJ4VXHDDdKGuZ9AAP6TUg="][[Mod[ToCharacterCode@StringTake[#<>#,5].{6,1,8,5,3},151,1]]]&

How it works

For a book beginning with character codes a,b,c,d,e (wrapping around if necessary) computes 6a+b+8c+5d+3e modulo 151, which happens to be unique, and then looks up the number of chapters in a compressed list of length 151. (Unused entries in the list are filled in with duplicates of the previous entry. This encourages run-length encoding, maybe? Anyway, it helps.)

Thanks to @numbermaniac for the list compression idea, which is hard to put a number on but is a huge part of the improvement here.

Old version: Mathematica, 548 435 407 bytes

Join["111112333333344444455555666789"~(p=StringPartition)~1,"1010121213131314141616162121222224242425272828293131343636404248505266"~p~2,{"150"}][[Max@StringPosition["2jn3jnjduoaapimhgi2pe2tshbkjeonhntttzpn2toclsjnjmlhpiprtu1jn1pe1tsjmjlmt1toehiglimcmsnoaometrerzdnlecs2cihbwnhihshzcr1cimrarmsjhojds1kgrva2sujsalku2kglviatcmte1co1supordur2conmreosjbbeeegnijriiahpas",""<>#&@Characters[#<>#][[{1,3,6}]]]/3]]&

How it works

We convert each name name to characters 1, 3, and 6 of namename (e.g. leviticus becomes lvi, job becomes jbb) before looking it up.

The list we look things up in is slightly compressed by putting the 1-digit and the 2-digit chapter numbers together in strings.

Mathematica: 40 bytes, non-competing

WolframAlpha["# chapters "<>#,"Result"]&

Yeah.

\$\endgroup\$
  • \$\begingroup\$ If you use Compress on the list, you get a string that can be turned back into the list with Uncompress in your program; the whole function comes out to 430 bytes, saving you 5 :) \$\endgroup\$ – numbermaniac Oct 3 '17 at 7:16
  • \$\begingroup\$ Thanks! That version of the program had better ways to shorten the list, but this worked out very well for the new approach I found :) \$\endgroup\$ – Misha Lavrov Oct 3 '17 at 15:19
  • 4
    \$\begingroup\$ I really hoped a second Mathematica answer would contain a Bible Built-in \$\endgroup\$ – SztupY Oct 3 '17 at 17:00
  • \$\begingroup\$ There's no built-in; there's WolframAlpha["number of chapters of "<>#,"Result"]&, but somehow that doesn't feel like quite the same thing. \$\endgroup\$ – Misha Lavrov Oct 3 '17 at 17:10
  • 1
    \$\begingroup\$ It looks up the answer on Wolfram Alpha, which is called out as cheating both in the standard loopholes and explicitly in the question. (Although I think that if I included Wolfram Alpha's source code and database in the byte count, that would make it okay again.) \$\endgroup\$ – Misha Lavrov Oct 3 '17 at 23:51
6
\$\begingroup\$

Python 2, 244 183 bytes

lambda n:ord('A("4,|O|;|>||_ /\xb5$+_>| _7|a|5-##_"_G"_/|7) $_$-|<|$_"&|||_;S_% |:_45_#||#|%7"_,_\'_)|_"|C_#|/_I|$_ "|,|_%|_8_Q|+||||_!|_C#'.replace('|','__')[hash(n)%839%434%152])-31

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Jelly,  117 115  114 bytes

OP%⁽µW%⁽£İ%237
“ĿḊƘ⁹ƙƬṂ;ɓṭ6-ạʋ⁵%$Ẋf,¥ÆÑƈø¬ȦṘd⁾“ʂụṿⱮẆƊ¦ẠLjƒ~Ḅr©“Ẏw|!¡¢o“ŻɗṢ“3ɠ‘Zċ€ÇTḢị“£ẆJQ+k⁽’ḃ6¤+\;“£¬®µıñø"BƝ¤‘¤

Try it online! or see the test-suite

How?

Hashes the product of the ordinals of the characters of the input string by taking three two three division remainders, looks up the result in a list of lists and uses the found index to look up the result in a list of book lengths.

When finding a hash function I only considered those which resulted in at most one bucket with any results over 255 to allow code-page indexing and then chose ones that minimised the total number of values to encode (after removing the "offending" bucket or if none existed the longest bucket). From 66 with three modulos I found a 59 (%731%381%258) a 58 (%731%399%239) then one with 56 entries (%1241%865%251) [making for 117 bytes] ...I then found a 58 using only two remainders (%1987%251) [making for 115 bytes]
...then I found a 55 using three remainders, which when two dummy entries are added allows for a further compression of the lookup list...

The code:

1.

“ĿḊƘ⁹ƙƬṂ;ɓṭ6-ạʋ⁵%$Ẋf,¥ÆÑƈø¬ȦṘd⁾“ʂụṿⱮẆƊ¦ẠLjƒ~Ḅr©“Ẏw|!¡¢o“ŻɗṢ“3ɠ‘

is a list of five lists of code-page indices (“...“...“...“...“...“...‘):

[[199,193,148,137,161,152,179,59,155,224,54,45,211,169,133,37,36,208,102,44,4,13,16,156,29,7,190,204,100,142],[167,225,226,149,207,145,5,171,76,106,158,126,172,114,6],[209,119,124,33,0,1,111],[210,157,183],[51,159]]

This is transposed using the atom Z to get the buckets; call this B:

[[199,167,209,210,51],[193,225,119,157,159],[148,226,124,183],[137,149,33],[161,207,0],[152,145,1],[179,5,111],[59,171],[155,76],[224,106],[54,158],[45,126],[211,172],[169,114],[133,6],37,36,208,102,44,4,13,16,156,29,7,190,204,100,142]

(the 0 and 1 are the dummy keys, allowing the [179,5,111] to be two further to the right - the transpose requires longer entries to be to the left)

2.

“£ẆJQ+k⁽’ḃ6¤+\;“£¬®µıñø"BƝ¤‘¤

Call this C (the chapter counts) - it's a list of integers:

[1,4,5,6,10,12,13,14,16,21,22,24,28,31,36,40,42,48,50,52,2,7,8,9,25,27,29,34,66,150,3]

and is constructed as follows (the two dummy keys above therefore allow 10,12,13 to be in ascending order):

“£ẆJQ+k⁽’ḃ6¤+\;“£¬®µıñø"BƝ¤‘¤
                            ¤ - nilad followed by link(s) as a nilad:
           ¤                  -   nilad followed by link(s) as a nilad:
“£ẆJQ+k⁽’                     -     base 250 number = 935841127777142
         ḃ6                   -     converted to bijective base 6 = [1,3,1,1,4,2,1,1,2,5,1,2,4,3,5,4,2,6,2,2]
            +\                -     cumulative reduce with addition = [1,4,5,6,10,12,13,14,16,21,22,24,28,31,36,40,42,48,50,52]
               “£¬®µıñø"BƝ¤‘  -   code-page indices = [2,7,8,9,25,27,29,34,66,150,3]
              ;               -   concatenate = [1,4,5,6,10,12,13,14,16,21,22,24,28,31,36,40,42,48,50,52,2,7,8,9,25,27,29,34,66,150,3]

Now the simplified version of the code is:

OP%⁽µW%⁽£İ%237 - Link 1, hash function: list of characters   e.g. "genesis"
O              - cast to ordinals             [103,101,110,101,115,105,115]
 P             - product                                    160493569871250
   ⁽µW         - base 250 literal                                      3338
  %            - modulo                                                1050
       ⁽£İ     - base 250 literal                                      1699
      %        - modulo                                                1050
           237 - literal                                                237
          %    - modulo                                                 102

Bċ€ÇTḢịC - Main link: list of characters                     e.g. "genesis"
B        - the list of buckets, B, described  in (1) above
   Ç     - call the last link as a monad (get the hashed value)         102
 ċ€      - count for €ach - yields 29 zeros and a one or 30 zeros       [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0]
    T    - truthy indexes - yields a list of length one or zero         [19]
     Ḣ   - head - extracts that value or zero if it was empty            19  ----------------------------------------------v
       C - the list of chapter lengths, C, described in (2) above       [1,4,5,6,10,12,13,14,16,21,22,24,28,31,36,40,42,48,50,52,2,7,8,9,25,27,29,34,66,150,3]
      ị  - index (the head value) into (the chapter list, c)             50
         -       - 1-indexed and modular so 0 yields 3 (the rightmost)
\$\endgroup\$
3
\$\begingroup\$

Python 2, 438 429 416 411 409 bytes

lambda s:[c for x,c in zip('A|m|C|h|2|th|2 Co|D|y|E|Ep|Ec|x|ze|G|Ge|H|gg|He|Ho|I|Jo|oe|oh|na|sh|Ju|dg|Ja|Je| J|1 J|K|2 K|L|Le|Lu|M|ch|rk|tt|N|Ne|Nu|O|P|Pr|Ph|pp|Pe|2 P|R|Ro|Ru|S|Sa|2 S|T| T|2 T| Ti|2 Ti|Z|Zep'.split('|'),(28,9,4,29,36,16,13,12,34,10,6,12,40,48,6,50,3,2,13,14,66,42,3,21,4,24,1,21,5,52,1,5,22,25,5,27,24,7,4,16,28,3,13,36,1,150,31,1,4,5,3,22,16,4,8,31,24,3,5,3,6,4,14,3))if x in s.title()][-1]

Try it online!

Works by changing the input to Title Case, and finding the last matching substring in the list.

[('A', 28), ('m', 9), ('C', 4), ('h', 29), ('2', 36), ('th', 16), ('2 Co', 13), ...

Eg. '1 samuel' -> '1 Samuel' which matches ('m', 9), ('2', 36), ('S', 8), ('Sa', 31), ('2 S', 24). The last match is ('2 S', 24), so the answer is 24

\$\endgroup\$
3
\$\begingroup\$

6502 machine code (C64), 204 bytes

00 C0 20 FD AE 20 9E AD 85 FD 20 A3 B6 A9 1E 85 FB A9 00 85 FC A8 B1 22 18 69
4A 65 FC 45 FB 85 FC E6 FB C8 C4 FD D0 EE A5 FC 4A 4A 4A 4A A8 A5 FC 29 0F 19
BA C0 A8 BE 3D C0 A9 00 4C CD BD 16 34 42 0D 01 00 04 03 04 1C 0A 00 06 15 07
00 16 00 22 03 02 0E 00 24 00 00 01 00 08 03 00 1C 03 01 00 00 00 00 00 03 03
00 00 00 24 10 1F 18 0E 10 00 00 00 32 30 1F 2A 00 0D 00 05 00 1B 00 0A 00 01
28 00 00 0C 96 00 10 00 00 00 18 00 00 03 00 00 00 00 00 00 15 09 00 05 00 04
00 00 04 00 00 04 00 00 18 00 1D 05 00 00 19 00 0D 00 00 06 06 0C 00 00 00 00
05 00 01 00 05 00 04 30 10 20 10 40 70 00 00 20 50 00 10 60 30 20

Explanation:

The key here is to use a special hashing function that maps without collissions to values 0 to 125 *). The chapter numbers are then placed in a 126 bytes table. The hashing is done in full 8 bits, the final value is adjusted by looking up the high nibbles in yet another table, this way combining different high nibbles where the low nibbles don't collide.

Here's a commented disassembly listing of the code part:

.C:c000  20 FD AE    JSR $AEFD          ; consume comma
.C:c003  20 9E AD    JSR $AD9E          ; evaluate argument
.C:c006  85 FD       STA $FD            ; remember string length
.C:c008  20 A3 B6    JSR $B6A3          ; free string ....
.C:c00b  A9 1E       LDA #$1E           ; initial xor key for hash
.C:c00d  85 FB       STA $FB
.C:c00f  A9 00       LDA #$00           ; initial hash value
.C:c011  85 FC       STA $FC
.C:c013  A8          TAY
.C:c014   .hashloop:
.C:c014  B1 22       LDA ($22),Y        ; load next string character
.C:c016  18          CLC                ; clear carry for additions
.C:c017  69 4A       ADC #$4A           ; add fixed offset
.C:c019  65 FC       ADC $FC            ; add previous hash value
.C:c01b  45 FB       EOR $FB            ; xor with key
.C:c01d  85 FC       STA $FC            ; store hash value
.C:c01f  E6 FB       INC $FB            ; increment xor key
.C:c021  C8          INY                ; next character
.C:c022  C4 FD       CPY $FD            ; check for string length
.C:c024  D0 EE       BNE .hashloop      ; end of string not reached -> repeat
.C:c026   .hashadjust:
.C:c026  A5 FC       LDA $FC            ; load hash
.C:c028  4A          LSR A              ; shift left four times
.C:c029  4A          LSR A
.C:c02a  4A          LSR A
.C:c02b  4A          LSR A
.C:c02c  A8          TAY                ; and copy to y index register
.C:c02d  A5 FC       LDA $FC            ; load hash again
.C:c02f  29 0F       AND #$0F           ; mask low nibble
.C:c031  19 BA C0    ORA $C0BA,Y        ; and add high nibble from table
.C:c034  A8          TAY                ; copy to y index register
.C:c035  BE 3D C0    LDX .chapters,Y    ; load chapter number from table
.C:c038  A9 00       LDA #$00           ; accu must be 0 for OS function:
.C:c03a  4C CD BD    JMP $BDCD          ; print 16bit value in A/X

after that follows a table of the chapter numbers and finally a table of the high nibbles for the hash value.

Online demo

Usage: sys49152,"name", for example sys49152,"genesis" (output 50).

Important: If the program was load from disk (like in the online demo), issue a new command first! This is necessary because loading a machine program trashes some C64 BASIC pointers.

Hint about the casing: In the default mode of the C64, the input will appear as upper case. This is in fact lowercase, but the C64 has two modes and in the default upper/graphics mode, lowercase characters appear as uppercase and uppercase characters appear as graphics symbols.


*) of course, this isn't as dense as it could be ... oh well, maybe I find an even better solution later ;)

\$\endgroup\$
1
\$\begingroup\$

Java 8, 623 597 590 bytes

s->{int i=s.chars().map(c->c*c*c).sum()/13595;return s.contains("tt")?28:i<258?42:i<355?1:i<357?3:i<362?16:i<366?28:i<369?21:i<375?9:i<377?24:i<380?5:i<382?1:i<386?10:i<402?7:i<436?4:i<438?5:i<439?14:i<461?3:i<463?2:i<477?22:i<478?25:i<490?5:i<491?3:i<493?12:i<500?66:i<545?3:i<546?1:i<548?21:i<562?4:i<568?24:i<570?10:i<572?31:i<573?24:i<582?16:i<583?150:i<607?40:i<629?48:i<639?50:i<663?13:i<675?3:i<677?36:i<679?52:i<734?1:i<735?6:i<736?4:i<775?14:i<785?6:i<796?3:i<800?31:i<812?6:i<876?27:i<910?29:i<911?36:i<940?22:i<1018?4:i<1035?16:i<1036?13:i<1066?12:i<1092?34:i<1229?5:i<1230?3:8;}

-7 bytes thanks to @Nevay by changing the for-loop to a stream.

Can definitely be golfed more.. Just need to do some more testing.
It may not be the shortest answer by a long shot, and could be golfed by porting some existing answer, but I'm still proud to come up with something myself.. :)

Explanation:

Try it here.

  1. Takes the power of 3 of each character (as ASCII value) in the input String
  2. Takes the sum of those
  3. Divides the result by 13595 as integer division (in Java this automatically truncates/floors the result).
  4. This leaves 65 unique values (only habakkuk and matthew both have a value of 674)
  5. And then one giant ternary-if to return the correct result (with a few values combining in a single ternary-statement where possible (381 and 382 both 1; 425 and 436 both 4; 649 and 663 both 13; 952 and 1018 both 4; 1122 and 1229 both 5).
\$\endgroup\$
  • 1
    \$\begingroup\$ You can use int i=s.chars().map(c->c*c*c).sum()/13595; instead of int i=0;for(int c:s.getBytes())i+=c*c*c;i/=13595;. \$\endgroup\$ – Nevay Oct 4 '17 at 22:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.