# How much Mana do I need?

Dungeon Master was one of the first ever real-time role-playing games, originally released in 1987 on the Atari ST. Among other exciting things for the time, it offered a rather sophisticated spell system based on runes.

Your task today is to write a program or function that evaluates the number of Mana points required to cast a given spell in Dungeon Master.

The 'spell cast' system is the top right cyan box in the above picture.

## Spells, runes and Mana

Dungeon Master spells are composed of 2 to 4 runes, picked among the following categories, in this exact order:

1. Power (mandatory)
2. Elemental Influence (mandatory)
3. Form (optional)
4. Class / Alignment (optional)

It means that valid spells are either:

• Power + Elemental Influence
• Power + Elemental Influence + Form
• Power + Elemental Influence + Form + Class / Alignment

Each category contains 6 runes, and each rune has an associated base Mana cost:

=============================================================================
| Power               | Rune      |   Lo |   Um |   On |   Ee |  Pal |  Mon |
|                     +-----------+------+------+------+------+------+------+
|                     | Base cost |    1 |    2 |    3 |    4 |    5 |    6 |
=============================================================================
| Elemental Influence | Rune      |   Ya |   Vi |   Oh |  Ful |  Des |   Zo |
|                     +-----------+------+------+------+------+------+------+
|                     | Base cost |    2 |    3 |    4 |    5 |    6 |    7 |
=============================================================================
| Form                | Rune      |  Ven |   Ew | Kath |   Ir |  Bro |  Gor |
|                     +-----------+------+------+------+------+------+------+
|                     | Base cost |    4 |    5 |    6 |    7 |    7 |    9 |
=============================================================================
| Class / Alignment   | Rune      |   Ku |  Ros | Dain | Neta |   Ra |  Sar |
|                     +-----------+------+------+------+------+------+------+
|                     | Base cost |    2 |    2 |    3 |    4 |    6 |    7 |
=============================================================================


## Evaluating the Mana cost

The Mana cost of the spell is the sum of the Mana cost of all runes:

• The cost of the Power rune always equals its base cost (from 1 to 6).

• For the other runes, the following formula applies:

where power is the base cost of the Power rune.

## Examples

Spell: Lo Ful
Cost : 1 + floor((1 + 1) * 5 / 2) = 1 + 5 = 6

Spell: Um Ful
Cost : 2 + floor((2 + 1) * 5 / 2) = 2 + 7 = 9

Spell: Pal Vi Bro
Cost : 5 + floor((5 + 1) * 3 / 2) + floor((5 + 1) * 7 / 2) = 5 + 9 + 21 = 35


## Clarifications and rules

• Your input will consist of 2 to 4 strings designating the runes of the spell. You can take them in any reasonable format, such as 4 distinct parameters, an array of strings (e.g. ['Lo', 'Ful']), or just one string with a single-character separator of your choice (e.g. 'Lo Ful'). Please specify the selected input format in your answer.
• The runes are guaranteed to be valid.
• The order of the categories must be respected. Unused categories may be either missing or replaced with some falsy value.
• You can accept the runes in any of these formats: 1. A capital letter followed by lower case ('Ful') 2. All lower case ('ful') 3. All upper case ('FUL'). But you can't mix different formats.
• Quite obviously, we do not care to know whether the spell actually has some effect in the game (for the curious, useful spells are listed here.)
• This is , so the shortest code in bytes wins.
• And remember: Lord Chaos is watching you!

## Test cases

Spell          | Output
---------------+-------
Lo Ful         | 6
Um Ful         | 9
On Ya          | 7
Lo Zo Ven      | 12
Pal Vi Bro     | 35
Ee Ya Bro Ros  | 31
On Ful Bro Ku  | 31
Lo Zo Kath Ra  | 20
On Oh Ew Sar   | 35
Ee Oh Gor Dain | 43
Mon Zo Ir Neta | 68
Mon Des Ir Sar | 75

• Tangential - but for people that like this system, The Magical Land of Wozz is a Japanese SNES game (English translation available) that implements nearly the same system - where any string of letters becomes a spell. google.co.jp/webhp?ie=UTF-8#q=magical+land+of+wozz Jun 30, 2017 at 7:10
• I have a vague memory of watching someone play Dungeon Master (I don't think it was on the ST). They had equipped one of their fighters with a magic necklace, and every so often they would check whether it had recharged and if so cast another rune of what I think was a light producing spell of some sort, so that the fighter was able to cast this spell every ten minutes or so, and eventually gained a level in wizardry.
– Neil
Jun 30, 2017 at 8:39
• @Neil This necklace was probably either the Moonstone (+3 Mana) or the Pendant Feral (+1 Wizard Skill). All items are listed here. Jun 30, 2017 at 8:55
• this script should output all possible input combinations
– Maya
Jul 1, 2017 at 12:43

# Python 2, 135119 115 bytes

b=[int('27169735 2  4567 435262'[int(x,36)%141%83%50%23])for x in input()]
print b[0]+sum(a*-~b[0]/2for a in b[1:])


Try it online!

Input is list of strings from stdin

• Outgolfed... >.< Jun 29, 2017 at 13:16
• I have copied your TIO input method to my answer as well, hope you don't mind. Jun 29, 2017 at 13:28
• @Mr.Xcoder I have now included all test cases. You can copy them if you want to
– ovs
Jun 29, 2017 at 13:38
• Oo, nicely done on the indexing - mind if I borrow it for my JS solution? Jun 29, 2017 at 15:01

# 05AB1E, 83 82 bytes

.•Y<εΔ•¹нk©.•M₄Pç•²нkÌ.•Jrû •³нkD(i\ë4 3‡4+}.•A1Δ#•I4èkD(i\ë3LJ012‡Ì})ćsv®>y*;(î(+


Try it online!

-1 thanks to Emigna.

SOOOOOOO ungolfed :(

Explanation:

.•Y<εΔ•¹нk©.•M₄Pç•²нkÌ.•Jrû •³нkD(i\ë4 3‡4+}.•A1Δ#•I4èkD(i\ë3LJ012‡Ì})ćsv®>y*;(î(+ Accepts four runes as separate lines, lowercase. Use Ø for missing runes.
.•Y<εΔ•¹нk©                                                                        Index first letter of first rune into "aluoepm" ("a" makes 1-indexed)
.•M₄Pç•²нkÌ                                                             Index first letter of second rune into "yvofdz", 2-indexed.
.•Jrû •³нkD(i\ë4 3‡4+}                                       Index first letter of third rune into "vekibg", 0-indexed, if it's not there pop, else, if index is 4 replace with 3, else keep as-is, then increment by 4.
.•A1Δ#•I4èkD(i\ë3LJ012‡Ì}              Index fourth letter (modular wrapping) of fourth rune into "kodnra", if it's not there pop, else, if index is one of 1, 2 or 3, replace with 0, 1 or 2 respectively, else keep as-is, then increment by 2.
)ćs           Wrap all numbers into a list, keeping the power rune behind.
v          For each
®>y*;(î(   Apply the formula

• One small golf would be that if you index into .•Y<εΔ• at the beginning, you don't need to increment the index. Jun 29, 2017 at 14:27
• @Emigna Ooh didn't have time to try that yet... Jun 29, 2017 at 14:28

# JavaScript (ES6), 15715611611210099 97 bytes

Takes input as an array of strings.

a=>a.map(c=>t+=(v=+27169735020045670435262[parseInt(c,36)%141%83%50%23],+a?a*v+v>>1:a=v),t=0)|t

• Saved a massive 44 bytes by borrowing the indexing trick from ovs' Python solution - if you're upvoting this answer, please upvote that one too.
• Saved 13 bytes thanks to Arnauld pointing out what should have been an obvious opportunity to use a ternary.

Try it Online!

## Explanation

Hoo, boy, this is gonna be fun - my explanations to trivial solutions suck at the best of times! Let's give it a go ...

a=>


An anonymous function taking the array as an argument via parameter a.

a.reduce((t,c)=>,0)


Reduce the elements in the array by passing each through a function; the t parameter is the running total, the c parameter is the current string and 0 is the initial value of t.

parseInt(c,36)


Convert the current element from a base 36 string to a decimal integer.

%141%83%50%23


Perform a few modulo operations on it.

+27169735 2  4567 435262[]


Grab the character from the string at that index and convert it to a number.

v=


Assign that number to variable v.

+a?


Check if variable a is a number. For the first element a will be the array of strings, trying to convert that to a number will return NaN, which is falsey. On each subsequent pass, a will be a positive integer, which is truthy.

a*v+v>>1


If a is a number then we multiply it by the value of v, add the value of v and shift the bits of the result 1 bit to the right, which gives the same result as dividing by 2 and flooring.

:a=v


If a is not a number the we assign the value of v to it, which will also give us a 0 to add to our total on the first pass.

t+


Finally, we add the result from the ternary above to our running total.

## Original, 156 bytes

a=>a.reduce((t,c)=>t+(p+1)*g(c)/2|0,p=(g=e=>+123456234567456779223467["LoUmOnEePaMoYaViOhFuDeZoVeEwKaIrBrGoKuRoDaNeRaSa".search(e[0]+e[1])/2])(a.shift()))

• You can save one more byte by reusing a: a=>a.reduce((t,c)=>t+(v=+'27169735020045670435262'[parseInt(c,36)%141%83%50%23],+a?a*v+v>>1:a=v),0) (EDIT: removed an irrelevant comment about integers passed in the input -- it seems like I didn't understand my own challenge very well ^^) Jun 30, 2017 at 14:38
• Neat trick, thanks, @Arnauld. Trying to come up with a calculation that would give me the number we're indexing in less bytes at the moment but not having much luck. Jun 30, 2017 at 15:07

(?<=^(1+) .*1)
$1 \G1 11 11  Try it online! Link includes test cases. Takes space-separated runes. Explanation: The initial stages simply convert each rune to a digit, which is then converted to unary. The numbers after the first are multiplied by one more than the first number, following which the first number is doubled. The final stage integer divides all the result by 2 and takes the sum. # Go, 205 bytes func c(s []string)int{f,l:=strings.IndexByte,len(s) p:=f("UOEPM",s[0][0])+3 r:=p-1+p*(f("VOFDZ",s[1][0])+3)/2 if l>2{r+=p*(f("war o",s[2][1])+5)/2} if l>3{r+=p*(f("it Ra",s[3][len(s[3])-2])+3)/2} return r}  It's a callable function, takes runes as a slice of strings, e.g. []string{"Um", "Ful"}. Try it on the Go Playground. # C, 222 Thanks to @ceilingcat for some very nice pieces of golfing - now even shorter i;b(r,s)char**r;{for(i=17;i--&&"& ;$ # 4 %        ; B * 6 $8 6 5 - >3 + A@( . 6 5 "[s*17+i]-3-(*r[s]^r[s][1]););return~i?i/2+1:0;}main(n,r)int**r;{n=b(++r,0);printf("%d\n",n-(b(r,1)+b(r,2)+b(r,3))*~n/2);}  Try it online! A more ungolfed version of my original answer: #include<stdio.h> char*a="& ;$ # 4 %      "
"  ; B * 6 $8 " " 6 5 - >3 +" " A@( . 6 5 "; int b(char*r,int s){ for(int i=0;i<17;i++) if(a[i+s]-3==(r[0]^r[1])) return i/2+1; return 0; } #define c(p,r,i)(p+1)*b(r[i+1],i*17)/2 int main(int n,char**r){ int x=b(r[1],0); printf("%d\n",x+c(x,r,1)+c(x,r,2)+c(x,r,3)); }  You need to supply four command line arguments - so for the first test case you need to run ./a.out Lo Ful "" "" • @ceilingcat Good job getting rid of the #define Sep 11, 2020 at 4:58 • @ceilingcat Good catch! Jul 17 at 21:57 ## Haskell, 623 bytes Using ADTs instead of numerical voodoo. NOTE: Add 36 for {-# LANGUAGE GADTs,ViewPatterns #-} • Score is computed assuming that it is compiled/run with -XGADTs -XViewPatterns data P=Lo|Um|On|Ee|Pal|Mon deriving(Enum,Read,Eq) data E=Ya|Vi|Oh|Ful|Des|Zo deriving(Enum,Read,Eq) data F=Ven|Ew|Kath|Ir|Bro|Gor deriving(Enum,Read,Eq) data C=Ku|Ros|Dain|Neta|Ra|Sar deriving(Enum,Read,Eq) data S where S::P->E->S Q::P->E->F->S R::P->E->F->C->S k(a:b:x)=let{p=read a;e=read b}in case x of{[]->S p e;[c]->Q p e(read c);[c,d]->R p e(read c)(read d)} c,d::Enum a=>a->Int c=succ.fromEnum d=(+2).fromEnum e Bro=7 e x=(+2).d$x
f x|c xelem[1,5,6]=d x|2>1=c x
g p f x =(div2).(*f x).succ$c p h(S x y)=c x+g x d y h(Q x y z)=h(S x y)+g x e z h(R x y z t)=h(Q x y z)+g x f t main=print.h.k.words=<<getLine Input: a single spell as a normal string e.g. Lo Ful Um Ful Multilining can be done by replacing the last line with main=interact$unlines.map(show.h.k.words).lines

But this would add bytes to the count