Challenge

Given an ASCII representation of a Babylonian number as input, output the number in Western Arabic numerals.

Babylonian Numeral System

How did the Babylonians count? Interestingly, they used a Base 60 system with an element of a Base 10 system. Let's first consider the unit column of the system:

The Babylonians had only three symbols: T (or, if you can render it: 𒐕) which represented 1, and < (or, if you can render it: 𒌋) which represented 10, and \ (or, if you render it: 𒑊) which represented zero.

Note: Technically, \ (or 𒑊) isn't zero (because the Babylonians did not have a notion of 'zero'). 'Zero' was invented later, so \ was a placeholder symbol added later to prevent ambiguity. However, for the purposes of this challenge, it's enough to consider \ as zero

So, in each column you just add up the value of the symbols, e.g.:

<<< = 30
<<<<TTTTTT = 46
TTTTTTTTT = 9
\ = 0

There will never be more than five < or more than nine T in each column. \ will always appear alone in the column.

Now, we need to extend this to adding more columns. This works exactly the same as any other base sixty, where you multiply the value of the rightmost column by \$60^0\$, the one to the left by \$60^1\$, the one to the left by \$60^2\$ and so on. You then add up the value of each to get the value of the number.

Columns will be separated by spaces to prevent ambiguity.

Some examples:

<< <TT = 20*60 + 12*1 = 1212
<<<TT \ TTTT = 32*60^2 + 0*60 + 4*1 = 115204

Rules

  • You are free to accept either ASCII input (T<\) or Unicode input (𒐕𒌋𒑊)
  • The inputted number will always be under \$10^7\$
  • The <s will always be to the left of the Ts in each column
  • \ will always appear alone in a column

Winning

Shortest code in bytes wins.

  • May we assume that the < will always be to the left of any Ts in a given column? – Taylor Scott Aug 13 at 11:05
  • 2
    @TaylorScott Yes, you may – Beta Decay Aug 13 at 11:17
  • 2
    In case it helps: Max that needs to be handled is 4 columns: <<<<TTTTTT <TTTTTTT <<<<TTTTTT <<<< – Wernisch Aug 13 at 11:40
  • 4
    Foreign types with the hookah pipes say Ay oh whey oh, ay oh whey oh - Count like a Babylonian. Great. Now it's stuck in my head. – cobaltduck Aug 14 at 20:50
  • 4
    "How did the Babylonians count? Interestingly, they used a Base 60 system with an element of a Base 10 system." Which is still in use today; the Babylonian number system is exactly what we use for clocks. Two decimal digits each for seconds, minutes, and hours, 60 seconds to the minute, 60 minutes to the hour. – Ray Aug 14 at 21:20

23 Answers 23

JavaScript (ES6), 44 bytes

Takes input as an array of ASCII characters.

a=>a.map(c=>k+=c<1?k*59:c<'?'?10:c<{},k=0)|k

Try it online!

How?

The Babylonian Numeral System can be seen as a 4-instruction language working with a single register -- let's call it the accumulator.

Starting with \$k=0\$, each character \$c\$ in the input array \$a\$ modifies the accumulator \$k\$ as follows:

  • space: multiply \$k\$ by \$60\$ (implemented as: add \$59k\$ to \$k\$)
  • <: add \$10\$ to \$k\$
  • T: increment \$k\$
  • \: do nothing; this is the NOP instruction of this language (implemented as: add \$0\$ to \$k\$)

Perl 6, 39 bytes

-3 bytes thanks to nwellnhof

{:60[.words>>.&{sum .ords X%151 X%27}]}

Try it online!

Uses the cuneiform characters.

Explanation:

{                                     }   # Anonymous code block
     .words  # Split input on spaces
           >>.&{                    }  # Convert each value to
                sum   # The sum of:
                    .ords # The codepoints
                          X%151 X%27   # Converted to 0,1 and 10 through modulo
 :60[                                ]  # Convert the list of values to base 60
  • You beat me by a couple of minutes. Here's what I came up with: {:60[.words>>.&{sum (.ords X%151)X%27}]} (40 bytes) – nwellnhof Aug 13 at 10:23
  • @nwellnhof Very well done! How did you find the mod values? – Jo King Aug 13 at 10:31
  • 2
    Simply by brute force. – nwellnhof Aug 13 at 10:34

Jelly,  13  12 bytes

ḲO%7C%13§ḅ60

A monadic link accepting a list of characters which yields an integer.

Try it online!

How?

ḲO%7C%13§ḅ60 - Link: list of characters   e.g. "<<<TT \ TTTT"
Ḳ            - split at spaces                 ["<<<TT", "\", "TTTT"]
 O           - cast to ordinals                [[60,60,60,84,84],[92],[84,84,84,84]]
  %7         - modulo by seven (vectorises)    [[4,4,4,0,0],[1],[0,0,0,0]]
    C        - compliment (1-X)                [[-3,-3,-3,1,1],[0],[1,1,1,1]]
     %13     - modulo by thirteen              [[10,10,10,1,1],[0],[1,1,1,1]]
        §    - sum each                        [32,0,4]
         ḅ60 - convert from base sixty         115204

Another 12: ḲO⁽¡€%:5§ḅ60 (⁽¡€ is 1013, so this modulos 1013 by the Ordinal values getting 53, 5, and 1 for <, T, \ respectively then performs integer division, : by 5 to get 10, 1 and 0)

  • Lol, I've deleted my answer exactly because of this, since I remembered I could use base conversion but was literally too lazy to find out how. +1 – Mr. Xcoder Aug 13 at 11:49

C (gcc), 140 138 bytes

B,a,b,y;l(char*o){y=B=0,a=1;for(char*n=o;*n;n++,B++[o]=b,a*=60)for(b=0;*n&&*n-32;)b+=!(*n-84)+10*!(*n++-60);for(B=a;B/=60;y+=*o++*B);B=y;}

Try it online!

  • 6
    +1 Taking "count like a babylonian" to the next level :D – Beta Decay Aug 13 at 13:55

Python 2, 96 93 91 bytes

lambda s:sum(60**i*(sum((ord(c)%5or 30)/3for c in v))for i,v in enumerate(s.split()[::-1]))

Try it online!


Saved:

  • -1 byte, thanks to Mr. Xcoder
  • 1
    95: (ord(c)%5/2or 11)-1 – Mr. Xcoder Aug 13 at 10:03
  • @Mr.Xcoder Thanks :) – TFeld Aug 13 at 10:32
  • 2
    87: 8740%ord(c)/4 – Poon Levi Aug 13 at 11:23

05AB1E, 13 bytes

8740|Ç%4/O60β

Try it online!

To make up for how lazy I've been with my Jelly answer, here is a submission in 05AB1E xD.

  • Help 05AB1E-ers out there, wasn't there a way to compress numbers like 8740? – Mr. Xcoder Aug 13 at 12:28
  • 2
    codegolf.stackexchange.com/a/166851/52210 Unfortunately it wouldn't be shorter: •Yη• (4 bytes) – Kevin Cruijssen Aug 13 at 12:29
  • 2
    @KevinCruijssen Thank you! That answer is very useful, I'll totally use it in the future – Mr. Xcoder Aug 13 at 12:30
  • 1
    Glad the tip is of use. :) I figured these things out after seeing some answers using them. The dictionary part was explained here. And the compression of other strings or large integers I figured out myself after seeing the linked example answers for "goose" and 246060. – Kevin Cruijssen Aug 13 at 12:35
  • 1|Ç7%-13%O60β is also 13 - is it golfable? – Jonathan Allan Aug 13 at 19:25

Excel VBA, 121 bytes

Restricted to 32-Bit Office as ^ serves as the LongLong type literal in 64-Bit versions

Takes input from cell A1 and outputs to the vbe immediate window.

a=Split([A1]):u=UBound(a):For i=0 To u:v=a(i):j=InStrRev(v,"<"):s=s+(j*10-(InStr(1,v,"T")>0)*(Len(v)-j))*60^(u-i):Next:?s

Ungolfed and Commented

a=Split([A1])       '' Split input into array
u=UBound(a)         '' Get length of array
For i=0 To u        '' Iter from 0 to length
v=a(i)              '' Get i'th column of input
j=InStrRev(v,"<")   '' Get count of <'s in input
                    '' Multiply count of <'s by 10; check for any T's, if present
                    ''   add count of T's
t=t+(j*10-(InStr(1,v,"T")>0)*(Len(v)-j))
    *60^(u-i)       '' Multiply by base
Next                '' Loop
?s                  '' Output to the VBE immediate window

Python 2, 62 bytes

lambda s:reduce(lambda x,y:x+[10,0,59*x,1]["<\ ".find(y)],s,0)

Try it online!

This uses the technique from Arnauld's answer.

Canvas, 20 17 16 bytes

S{{<≡AײT≡]∑]<c┴

Try it here!

Explanation:

E{          ]     map over input split on spaces
  {       ]         map over the characters
   <≡A×               (x=="<") * 10
       ²T≡            x=="T"
           ∑        sum all of the results
             <c┴  and encode from base (codepoint of "<") to 10

Dyalog APL, 33 30 bytes

{+/(⌊10*⍵-3)×60*+\2=⍵}'\ T<'⍳⌽

Try it online!

Edit: -3 bytes thanks to ngn

'\ T<'⍳ replaces the characters with numbers (their position in the string constant), and reverses the input so most significant 'digits' are last. This allows +\2= to keep a running count of the desired power of 60 (applied by 60*) by counting the number of times a space (index 2 in the string constant) is encountered.

⌊10*⍵-3 gives the desired power of ten for each character. The order of characters in the string constant and the -3 offset cause '\' and space to go to negative numbers, resulting in fractions when those characters are raised to the power of 10, allowing them to be eliminated by .

All we have to do now is multiply the powers-of-10 digits by the powers-of-60 place values and sum the lot up with +/.

  • save a few bytes by avoiding the separate comparison with ' ': {+/(⌊10*⍵-3)×60*+\2=⍵}'\ T<'⍳⌽ – ngn Aug 26 at 9:16

JavaScript (Node.js), 122 114 107 106 83 bytes

a=>a.split` `.map(b=>[...b].map(c=>x+=c<'T'?10:c<'U',x=0)&&x).reduce((a,b)=>a*60+b)

Try it online!

I'm a little obsessed with "functional-style" array operations, uses ASCII input, as far as I can tell, JS isn't very good at getting charcodes golfily

I'm keeping this for posterity's sake, but this is a naive/dumb solution, I suggest you check out Arnauld's answer which is far more interesting an implementation of the challenge

  • @Shaggy looks like it works to me! – Skidsdev Aug 13 at 10:45
  • c<'T' works in place of c=='<' – Mr. Xcoder Aug 13 at 10:47
  • Save 1 more by replacing && with |. – Shaggy Aug 13 at 13:03
  • @Shaggy and save a lot more by using for...of loops :P – ASCII-only Aug 13 at 21:50

Retina, 29 26 23 bytes

<
10*T
+`^(.*)¶
60*$1
T

Try it online! Uses newline separation, but link includes header to use spaces instead for convenience. Edit: Saved 3 bytes with help from @KevinCruijssen. Saved a further 3 bytes thanks to @FryAmTheEggman. Explanation:

<
10*T

Replace each < with 10 Ts.

+`^(.*)¶
60*$1

Take the first line, multiply it by 60, and add the next line. Then repeat until there is only one line left.

T

Count the Ts.

Faster 51-byte version:

%`^(<*)(T*).*
$.(10*$1$2
+`^(.+)¶(.+)
$.($1*60*_$2*

Try it online! Uses newline separation, but link includes header to use spaces instead for convenience. Explanation:

%`^(<*)(T*).*
$.(10*$1$2

Match each line individually, and count the number of Ts and 10 times the number of <s. This converts each line into its base-60 "digit" value.

+`^(.+)¶(.+)
$.($1*60*_$2*

Base 60 conversion, running a line at a time. The computation is done in decimal for speed.

  • I'm pretty sure the third line can be just < without the +, unless I'm not seeing some kind of edge case. – Kevin Cruijssen Aug 13 at 13:33
  • 1
    @KevinCruijssen Even better, as $& is now always one character, I can use the default character, saving a further two bytes! – Neil Aug 13 at 14:02
  • Ah nice! :) Didn't knew that could be done implicitly for single characters. – Kevin Cruijssen Aug 13 at 14:05
  • @KevinCruijssen Well, I don't care what the character is, as I'm only taking the length; in Retina 1 you get a _ while $* in earlier versions of Retina defaults to 1. – Neil Aug 13 at 14:09
  • Ah, I see. Your initial code was taking all < as single match and repeat them 10 times the length (the amount of < in the match), and my proposed change is repeating every < separately 10 times (which you've golfed by 2 bytes more using the implicit 1 with 10*). Now I better understand why the + was there initially. I don't know too much about the Retina builtins, only regexes in general, hence my proposed change because I already read it as repeat every > 10 times. ;) – Kevin Cruijssen Aug 13 at 14:16

Bash (with sed and dc), 50 bytes

sed 's/</A+/g
s/T/1+/g
s/ /60*/g
s/\\//g'|dc -ez?p

Takes space-delimited input from stdin, outputs to stdout

Try it online!

Explanation

Uses sed to transform the input with a bunch of regular expression matches until, for example, the input <<<TT \ TTTT has been transformed to A+A+A+1+1+60*60*1+1+1+1+. Then this input is fed to dc with the explicit input execution command ?, preceded by z (pushes the stack length (0) to the stack so that we have somewhere to ground the addition) and followed by p (print).

Dyalog APL, 35 bytes

x←0
{x+←('<T\ '⍳⍵)⌷10,1,0,59×x}¨⍞
x

Try it online!

Java 8, 64 bytes

a->{int r=0;for(int c:a)r+=c<33?r*59:c<63?10:c<85?1:0;return r;}

Try it online.

64 bytes alternative:

a->{int r=0;for(int c:a)r+=c<33?r*59:c<63?10:(c/87)^1;return r;}

Try it online.

Port of @Arnauld's JavaScript (ES6) answer.

Explanation:

a->{            // Method with character-array parameter and integer return-type
  int r=0;      //  Result-integer, starting at 0
  for(int c:a)  //  Loop over the input-array
    r+=         //   Increase the result by:
       c<33?    //    Is the current character a space:
        r*59    //     Increase it by 59 times itself
       :c<63?   //    Else-if it's a '<':
        10      //     Increase it by 10
       :c<85?   //    Else-if it's a 'T':
        1       //     Increase it by 1
       :        //    Else (it's a '\'):
        0;      //     Leave it unchanged by increasing it by 0
  return r;}    //  Return the result

Noether, 55 bytes

I~sL(si/~c{"<"=}{k10+~k}c{"T"=}{!k}c{" "=}{k60*~k}!i)kP

Try it online!

Same approach as @Arnauld.

R, 98 81 bytes

(u=sapply(scan(,""),function(x,y=utf8ToInt(x))y%%3%*%(y%%6)))%*%60^(sum(u|1):1-1)

Try it online!

Ridiculously long due to string parsing. Thanks Giusppe for shaving off 16 unnecessary bytes.

Define y the bytecode value of unicode input and R = y("T<\") = y("𒐕𒌋𒑊")

Observe that R%%3 = 1,2,0 and R%%6 = 1,5,0... so R%%3 * R%%6 = 1,10,0 !

The rest is easy: sum per column, then dot-product with decreasing powers of 60.

  • Porting Arnauld's asnwer using Reduce is likely to be more golfy. – JayCe Aug 13 at 17:13
  • doesn't scan(,"") split on spaces automatically? – Giuseppe Aug 13 at 17:43
  • 1
    nice trick with the mods, though! I was trying to figure that out but couldn't find it...and /60 can be replaced by -1 in the exponent expression for another byte off, plus the <- can be replaced by = since it's all in parentheses. – Giuseppe Aug 13 at 17:54
  • @Giuseppe I tried %%3 and it was promising so I kept looking... also using a dot product just saved me one additional byte :) – JayCe Aug 13 at 18:01

Ruby, 50 46 bytes

->a{x=0;a.bytes{|c|x+=[59*x,10,0,1][c%9%5]};x}

Try it online!

A basic port of Arnauld's answer improved by G B for -4 bytes.

  • 1
    45 bytes -actually 47 if you use "bytes" instead of "map" – G B Aug 13 at 15:36
  • Thanks @GB, I'll probably stick with the longer version, since taking input as raw bytecodes feels a bit too liberal for a language that normally supports strings. – Kirill L. Aug 13 at 16:33
  • 1
    Another byte off: 46 bytes – G B Aug 14 at 6:02

C (gcc), 65 64 63 bytes

f(s,o)char*s;{for(o=0;*s;s++)o+=*s>32?(93^*s)/9:o*59;return o;}

Try it online!

  • 1
    Replacing return o with s=o saves another 5 bytes. – ErikF Aug 14 at 7:06

J, 34 30 bytes

60#.1#.^:2(10 1*'<T'=/])&>@cut

Try it online!

Perl -F// -E, 39 bytes

$w+=/</?10:/T/?1:/ /?59*$w:0for@F;say$w

This reads the to be converted number from STDIN.

This is essential the same solution as given by @Arnauld using JavaScript.

Charcoal, 26 bytes

≔⁰θFS«≡ι ≦×⁶⁰θ<≦⁺χθT≦⊕θ»Iθ

Try it online! Link is to verbose version of code. Explanation:

≔⁰θ

Clear the result.

FS«...»

Loop over the input characters. The command is wrapped in a block to prevent it from finding a "default" block.

≡ι

Switch over the current character...

 ≦×⁶⁰θ

if it's a space then multiply the result by 60...

<≦⁺χθ

if it's a < then add 10 to the result...

T≦⊕θ

if it's a T then increment the result.

Iθ

Print the result.

F#, 128 bytes

let s(v:string)=Seq.mapFoldBack(fun r i->i*Seq.sumBy(fun c->match c with|'<'->10|'T'->1|_->0)r,i*60)(v.Split ' ')1|>fst|>Seq.sum

Try it online!

Ungolfed it would look like this:

let s (v:string) =
    Seq.mapFoldBack(fun r i ->
        i * Seq.sumBy(fun c ->
            match c with
                | '<' -> 10
                | 'T' ->1
                | _ -> 0
        ) r, 
        i * 60) (v.Split ' ') 1
    |> fst
    |> Seq.sum

Seq.mapFoldBack combines Seq.map and Seq.foldBack. Seq.mapFoldBack iterates through the sequence backwards, and threads an accumulator value through the sequence (in this case, i).

For each element in the sequence, the Babylonian number is computed (by Seq.sumBy, which maps each character to a number and totals the result) and then multiplied by i. i is then multiplied by 60, and this value is then passed to the next item in the sequence. The initial state for the accumulator is 1.

For example, the order of calls and results in Seq.mapFoldBack for input <<<TT \ TTTT would be:

(TTTT, 1)     -> (4, 60)
(\, 60)       -> (0, 3600)
(<<<TT, 3600) -> (115200, 216000)

The function will return a tuple of seq<int>, int. The fst function returns the first item in that tuple, and Seq.sum does the actual summing.

Why not use Seq.mapi or similar?

Seq.mapi maps each element in the sequence, and provides the index to the mapping function. From there you could do 60 ** index (where ** is the power operator in F#).

But ** requires floats, not ints, which means that you need to either initialise or cast all the values in the function as float. The entire function will return a float, which (in my opinion) is a little messy.

Using Seq.mapi it can be done like this for 139 bytes:

let f(v:string)=v.Split ' '|>Seq.rev|>Seq.mapi(fun i r->Seq.sumBy(fun c->match c with|'<'->10.0|'T'->1.0|_->0.0)r*(60.0**float i))|>Seq.sum

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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