17
\$\begingroup\$

Challenge

Forsyth–Edwards Notation (FEN) is a standard notation for describing a particular board position of a chess game. Your challenge is to evaluate the score using the FEN string. This is an example of a FEN string:

5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8

Using this string, you can calculate the material score for each color with the following score table:

  • p / P = Pawn = 1 point
  • n / N = Knight = 3 points
  • b / B = Bishop = 3 points
  • r / R = Rook = 5 points
  • q / Q = Queen = 9 points
  • k / K = King, these don't have any points because every legal position contains a king for each side

White pieces are designated using upper-case letters ("PNBRQK") while black pieces use lowercase ("pnbrqk"). Empty squares are noted using digits 1 through 8 (the number of empty squares), and "/" separates ranks.

From the example FEN string, we can calculate the material scores for each side:

For black:

5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8

All the black pieces left: p + p + p + p + r, this is a total of 9

For white:

5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8

All the white pieces left: P + R + P + N + P + P + P, this is a total of 13

The final score is determined with the following formula: White score - Black score = Final score, so for the example the final score would be: 13 - 9 = 4

Example :

Input:

5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8

Output:

4

All rules apply here, the solution with the least amount of bytes wins.


How to post

# Language Name, N bytes

 [code]

 [explaination, etc.]
\$\endgroup\$
  • 3
    \$\begingroup\$ So the actual position doesn't matter? You just count letters in the string? \$\endgroup\$ – xnor Oct 22 '15 at 23:13
  • 4
    \$\begingroup\$ Nitpick: That's not a complete FEN string. Also, kr1NQQQQ/2rNQQQQ/3NQQQQ/3NQQQQ/3NQQQQ/3NQQQQ/3NQQQQ/K2NQQQQ is winning for white, with black to move? :P \$\endgroup\$ – Doorknob Oct 22 '15 at 23:17
  • \$\begingroup\$ @xnor Yes, I think if the evaluation is also strategically based, it will get too complicated. You can also assume that all the inputs are legal positions, so don't worry about that. \$\endgroup\$ – Adnan Oct 22 '15 at 23:18
  • \$\begingroup\$ @Doorknob, yes, the score is only material based to simplify things \$\endgroup\$ – Adnan Oct 22 '15 at 23:19

14 Answers 14

3
\$\begingroup\$

CJam, 28 27 26 bytes

0l{i32mdD%[5ZZX9]=\3%(*+}/

Try it online in the CJam interpreter.

How it works

0l         e# Push a 0 (accumulator) and a line from STDIN.
{          e# For each character of that line:
  i32md    e#   Divide its code point by 32; push quotient and residue.
           e#   This serves two purposes:
           e#     1. The quotient will let us distinguish between uppercase
           e#        letters, lowercase letters and non-letters.
           e#     2. The residue will be the same for uppercase and lowercase
           e#        variants of the same letter.
  D%       e#   Take the residue modulo 13.
           e#   This maps R,N,B,P,Q -> 5,1,2,3,4
  [5ZZX9]= e#   Select the element at that index (5 ≡ 0) from [5 3 3 1 9].
  \        e#   Swap the quotient on top of the stack.
           e#   1 is digit or slash, 1 is uppercase, 2 is lowercase.
  3%(      e#   Take the quotient modulo 3 and subtract 1 from the result.
           e#   This maps 1,2,3 -> 0,1,-1.
  *+       e#   Multiply the generated integers.
  +        e#   Add the product to the accumulator.
}/         e#
\$\endgroup\$
5
\$\begingroup\$

><>, 64 57 56 53 bytes

"QRBNP"013359v
$0p4}:{:v?=1l<p4+' '{-
g4v?(0:i<+
n~<;

(-7 bytes with some inspiration from @El'endiaStarman's answer, -3 bytes thanks to @randomra)

Explanation

The program uses the codebox as a lookup table. Out-of-range puts/gets don't work with the online interpreter, so this only works with the official Python interpreter.

The first line pushes the pieces, followed by the piece values. It also pushes an initial 0 to kick off the total for the third line.

The second line then puts the appropriate positive or negative value at the corresponding piece cell, e.g. -1 is placed at ('p', 4) and 1 is placed ('P', 4). The length of the stack is checked to make sure the loop runs 5 times.

After the loop is over, the stack consists of our single zero from the first line. For each char we perform a lookup at the corresponding cell in the table and add it to our total. By default, uninitialised cell values are 0, which is perfect for our purposes.

The last line merely prints the result.

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

Ruby, 88 chars

->s{s.chars.map{|c|({P:1,N:3,B:3,R:5,Q:9}[:"#{c.upcase}"]||0)*(c.ord<90?1:-1)}.inject:+}

This is awkward and ugly, and there's probably a better way, but oh well.

Ruby's {foo: 'bar'} syntax is actually just sugar for {:foo => 'bar'}—this is annoying for golf because it means I have to convert the key to a symbol before using it to access a hash element (:"#{x}" is one char shorter than x.to_sym).

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

Pip, 39 bytes

I'll take my brief turn in the lead before the CJam and Pyth answers come along...

$+Y(95<=>A_)*013359@{"KPNBRQ"@?UCa|0}Ma

Takes the FEN string as a command-line argument. Here's an explanation for a slightly ungolfed version:

$+({(95<=>Aa)*013359@("KPNBRQ"@?UCa|0)}Ma)

   {                                  }Ma   Map this function to each character in input:
                                UCa          Uppercase version of character
                      "KPNBRQ"@?             Its index in this string, nil if not present
                                   |0        Logical or with 0 (to turn nil into 0)
              013359@(               )       Index into this number to get piece's score
          Aa                                 ASCII value of character
     95<=>                                   1 if less than 95, -1 if greater than 95
    (       )*                               Multiply by the score
$+(                                      )  Sum all scores and autoprint result
\$\endgroup\$
4
\$\begingroup\$

Perl, 44 bytes

#!perl -p
$\+=lc=~y/pnbrq/13359/r*(a cmp$_)for/\D/g}{

Counting the shebang as one, input is taken from stdin.


Sample Usage

$ echo 5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8 | perl fen-score.pl
4

Explanation

Pieces are transliterated with their respective values. If the piece is capitalized (i.e. less than a), its value is added to the sum, if not it is subtracted.

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

JavaScript ES7, 79 bytes 124 131

s=>(i=0,[for(q of s)i+={P:1,N:3,B:3,R:5,Q:9,p:-1,n:-3,b:-3,r:-5,q:-9}[q]||0],i)

As short as I can get. Uses fancy array comprehensions, to loop through the string.

Explanation

s=>(     // Define function with an argument

    i=0, // this var will store the score

    [for(q of s)   // Loops through input
      i+=          // Adds to score by...

         {P:1,...,   // Defines value of each letter
          p:-1,...}  // Negative value instead, which will subtract
         || 0        // Otherwise add 0

    ], i           // Return score
\$\endgroup\$
3
\$\begingroup\$

Minkolang 0.9, 72 65 64 60 44 42 41 bytes

13359"QRBNP"m5[d3~c~$r48*+0p0p]$I[o0q+]N.

Try it here.

Much thanks to Sp3000 for pointing out a much more efficient way to do this!

Explanation

13359"QRBNP"m pushes the scores and their corresponding characters, then interleaves them, so the stack looks like this: [1,80,3,78,3,66,5,82,9,81]. Then 5[d3~c~$r48*+0p0p] puts the score of each character, both lowercase and uppercase, at its location in the code space. Finally, $I[o0q+]N. loops through the input until it's empty, adding up the scores as it goes along.

\$\endgroup\$
2
\$\begingroup\$

CJam, 33 bytes

q{"PNBRQ"_el+#13359Ab_Wf*+0+=}%:+

Here's the super-naïve string-find way. Try it online.

\$\endgroup\$
2
\$\begingroup\$

Ouroboros, 82

Ouroboros is an esolang I designed this week. Time to take it for a spin!

i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!4*(4Sn1(

Each line of single-char commands1 represents an ouroboros snake, wherein execution proceeds from head (start) to tail (end) and loops back to the head. The ( and ) commands let you eat part of the tail or regurgitate it, thus changing what commands get executed. If the instruction pointer is ever swallowed, the snake dies (stops executing). An Ouroboros program consists of one or more snakes executing in parallel. Each snake has a stack of its own, and there is also a shared stack.

1One exception, which distinguishes Ouroboros from many 2D languages: multi-digit numbers can be written straightforwardly, without having to do math or push a 0 first.

Snake 1

The first snake reads a character (i) and checks whether it's -1 / EOF (.1+!). If so, it eats most of its tail, up to and including the M (57*().

The snake then swaps the character code with the tally that is above it on the stack (\), moves the tally to the shared stack (m), and swallows another character (1(). If it had already swallowed a bunch, this means it swallows the ( that the IP is currently on and dies. Otherwise, execution proceeds by moving the tally back to snake 1's stack, swapping it with the char code, and regurgitating the character that was previously swallowed (M\1)).

We then use math and stack operations to generate the appropriate score for the character. .96> tests whether it's lowercase or not; the subsequent 32*- converts to uppercase. Then the long stretch from .80= to 81=9*++++ maps P -> 1, N -> 3, etc. Finally, \2*1\-* negates the score if the letter was lowercase, and + adds it to the running tally. The snake then loops and reads another character.

Snake 2

The second snake starts with a regurgitate operation ()), which does nothing the first time through (since nothing's been swallowed yet, and also since popping an empty stack gives 0). Next, it pushes the length of the shared stack to its own stack and logically negates (L!). This gives 1 if the stack is empty, 0 otherwise. The snake multiplies by 4 and eats that many characters (4*().

If the shared stack was empty, this means the snake now ends before the S. It pushes 4 and loops back to the ), where it regurgitates the characters it just swallowed and starts over again.

If there was a value on the shared stack, however, no characters are swallowed and execution continues. The snake switches to the shared stack and outputs the number there (Sn); then it swallows its last character and dies (1().

Synchronization

The two snakes must be carefully synchronized so that there is never a value on the shared stack when snake 2 does its check, until the end of input is reached. Snake 1 puts a value on the shared stack briefly on each pass through its loop. Thus, snake 2's L command must never be executed between the m and M commands in snake 1. Fortunately, the snakes line up very well. Crucially, the length of snake 1's loop (70 instructions) is a multiple of snake 2's loop (7 instructions), so the two will never get out of sync:

i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5
          |__|
       Danger zone

If the numbers hadn't worked out so perfectly, I would have padded one or both snakes with spaces to make them align as needed.

All of this is very well, but I want to see it in action!

Here is the above program via Stack Snippet. Even on 1000 operations per second, it takes about 10 seconds to spit out the answer for the sample input--but it does get there!

// Define Stack class
function Stack() {
  this.stack = [];
  this.length = 0;
}
Stack.prototype.push = function(item) {
  this.stack.push(item);
  this.length++;
}
Stack.prototype.pop = function() {
  var result = 0;
  if (this.length > 0) {
    result = this.stack.pop();
    this.length--;
  }
  return result;
}
Stack.prototype.top = function() {
  var result = 0;
  if (this.length > 0) {
    result = this.stack[this.length - 1];
  }
  return result;
}
Stack.prototype.toString = function() {
    return "" + this.stack;
  }

// Define Snake class
function Snake(code) {
  this.code = code;
  this.length = this.code.length;
  this.ip = 0;
  this.ownStack = new Stack();
  this.currStack = this.ownStack;
  this.alive = true;
  this.wait = 0;
  this.partialString = this.partialNumber = null;
}
Snake.prototype.step = function() {
    if (!this.alive) {
      return null;
    }
    if (this.wait > 0) {
      this.wait--;
      return null;
    }
    var instruction = this.code.charAt(this.ip);
    var output = null;
    if (this.partialString !== null) {
      // We're in the middle of a double-quoted string
      if (instruction == '"') {
        // Close the string and push its character codes in reverse order
        for (var i = this.partialString.length - 1; i >= 0; i--) {
          this.currStack.push(this.partialString.charCodeAt(i));
        }
        this.partialString = null;
      } else {
        this.partialString += instruction;
      }
    } else if (instruction == '"') {
      this.partialString = "";
    } else if ("0" <= instruction && instruction <= "9") {
      if (this.partialNumber !== null) {
        this.partialNumber = this.partialNumber + instruction; // NB: concatenation!
      } else {
        this.partialNumber = instruction;
      }
      next = this.code.charAt((this.ip + 1) % this.length);
      if (next < "0" || "9" < next) {
        // Next instruction is non-numeric, so end number and push it
        this.currStack.push(+this.partialNumber);
        this.partialNumber = null;
      }
    } else if ("a" <= instruction && instruction <= "f") {
      // a-f push numbers 10 through 15
      var value = instruction.charCodeAt(0) - 87;
      this.currStack.push(value);
    } else if (instruction == "$") {
      // Toggle the current stack
      if (this.currStack === this.ownStack) {
        this.currStack = this.program.sharedStack;
      } else {
        this.currStack = this.ownStack;
      }
    } else if (instruction == "s") {
      this.currStack = this.ownStack;
    } else if (instruction == "S") {
      this.currStack = this.program.sharedStack;
    } else if (instruction == "l") {
      this.currStack.push(this.ownStack.length);
    } else if (instruction == "L") {
      this.currStack.push(this.program.sharedStack.length);
    } else if (instruction == ".") {
      var item = this.currStack.pop();
      this.currStack.push(item);
      this.currStack.push(item);
    } else if (instruction == "m") {
      var item = this.ownStack.pop();
      this.program.sharedStack.push(item);
    } else if (instruction == "M") {
      var item = this.program.sharedStack.pop();
      this.ownStack.push(item);
    } else if (instruction == "y") {
      var item = this.ownStack.top();
      this.program.sharedStack.push(item);
    } else if (instruction == "Y") {
      var item = this.program.sharedStack.top();
      this.ownStack.push(item);
    } else if (instruction == "\\") {
      var top = this.currStack.pop();
      var next = this.currStack.pop()
      this.currStack.push(top);
      this.currStack.push(next);
    } else if (instruction == "@") {
      var c = this.currStack.pop();
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(c);
      this.currStack.push(a);
      this.currStack.push(b);
    } else if (instruction == ";") {
      this.currStack.pop();
    } else if (instruction == "+") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(a + b);
    } else if (instruction == "-") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(a - b);
    } else if (instruction == "*") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(a * b);
    } else if (instruction == "/") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(a / b);
    } else if (instruction == "%") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(a % b);
    } else if (instruction == "_") {
      this.currStack.push(-this.currStack.pop());
    } else if (instruction == "I") {
      var value = this.currStack.pop();
      if (value < 0) {
        this.currStack.push(Math.ceil(value));
      } else {
        this.currStack.push(Math.floor(value));
      }
    } else if (instruction == ">") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(+(a > b));
    } else if (instruction == "<") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(+(a < b));
    } else if (instruction == "=") {
      var b = this.currStack.pop();
      var a = this.currStack.pop();
      this.currStack.push(+(a == b));
    } else if (instruction == "!") {
      this.currStack.push(+!this.currStack.pop());
    } else if (instruction == "?") {
      this.currStack.push(Math.random());
    } else if (instruction == "n") {
      output = "" + this.currStack.pop();
    } else if (instruction == "o") {
      output = String.fromCharCode(this.currStack.pop());
    } else if (instruction == "r") {
      var input = this.program.io.getNumber();
      this.currStack.push(input);
    } else if (instruction == "i") {
      var input = this.program.io.getChar();
      this.currStack.push(input);
    } else if (instruction == "(") {
      this.length -= Math.floor(this.currStack.pop());
      this.length = Math.max(this.length, 0);
    } else if (instruction == ")") {
      this.length += Math.floor(this.currStack.pop());
      this.length = Math.min(this.length, this.code.length);
    } else if (instruction == "w") {
      this.wait = this.currStack.pop();
    }
    // Any instruction not covered by the above cases is ignored
    if (this.ip >= this.length) {
      // We've swallowed the IP, so this snake dies
      this.alive = false;
      this.program.snakesLiving--;
    } else {
      // Increment IP and loop if appropriate
      this.ip = (this.ip + 1) % this.length;
    }
    return output;
  }

// Define Program class
function Program(source, speed, io) {
  this.sharedStack = new Stack();
  this.snakes = source.split(/\r?\n/).map(function(snakeCode) {
    var snake = new Snake(snakeCode);
    snake.program = this;
    snake.sharedStack = this.sharedStack;
    return snake;
  }.bind(this));
  this.snakesLiving = this.snakes.length;
  this.io = io;
  this.speed = speed || 10;
  this.halting = false;
}
Program.prototype.run = function() {
  if (this.snakesLiving) {
    this.step();
    this.timeout = window.setTimeout(this.run.bind(this), 1000 / this.speed);
  }
}
Program.prototype.step = function() {
  for (var s = 0; s < this.snakes.length; s++) {
    var output = this.snakes[s].step();
    if (output) {
      this.io.print(output);
    }
  }
}
Program.prototype.halt = function() {
  window.clearTimeout(this.timeout);
}
var ioFunctions = {
  print: function(item) {
    var stdout = document.getElementById('stdout');
    stdout.value += "" + item;
  },
  getChar: function() {
    if (inputData) {
      var inputChar = inputData[0];
      inputData = inputData.slice(1);
      return inputChar.charCodeAt(0);
    } else {
      return -1;
    }
  },
  getNumber: function() {
    while (inputData && (inputData[0] < "0" || "9" < inputData[0])) {
      inputData = inputData.slice(1);
    }
    if (inputData) {
      var inputNumber = inputData.match(/\d+/)[0];
      inputData = inputData.slice(inputNumber.length);
      return +inputNumber;
    } else {
      return -1;
    }
  }
};
var program = null;
var inputData = null;

function resetProgram() {
  var stdout = document.getElementById('stdout');
  stdout.value = null;
  if (program !== null) {
    program.halt();
  }
  program = null;
  inputData = null;
}

function initProgram() {
  var source = document.getElementById('source'),
    stepsPerSecond = document.getElementById('steps-per-second'),
    stdin = document.getElementById('stdin');
  program = new Program(source.value, +stepsPerSecond.innerHTML, ioFunctions);
  inputData = stdin.value;
}

function runBtnClick() {
  if (program === null || program.snakesLiving == 0) {
    resetProgram();
    initProgram();
  } else {
    program.halt();
    var stepsPerSecond = document.getElementById('steps-per-second');
    program.speed = +stepsPerSecond.innerHTML;
  }
  program.run();
}

function stepBtnClick() {
  if (program === null) {
    initProgram();
  } else {
    program.halt();
  }
  program.step();
}
.container {
  width: 100%;
}
.so-box {
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-weight: bold;
  color: #fff;
  text-align: center;
  padding: .3em .7em;
  font-size: 1em;
  line-height: 1.1;
  border: 1px solid #c47b07;
  -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 2px 0 rgba(255, 255, 255, 0.15) inset;
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
  background: #f88912;
  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 2px 0 rgba(255, 255, 255, 0.15) inset;
}
.control {
  display: inline-block;
  border-radius: 6px;
  float: left;
  margin-right: 25px;
  cursor: pointer;
}
.option {
  padding: 10px 20px;
  margin-right: 25px;
  float: left;
}
h1 {
  text-align: center;
  font-family: Georgia, 'Times New Roman', serif;
}
a {
  text-decoration: none;
}
input,
textarea {
  box-sizing: border-box;
}
textarea {
  display: block;
  white-space: pre;
  overflow: auto;
  height: 40px;
  width: 100%;
  max-width: 100%;
  min-height: 25px;
}
span[contenteditable] {
  padding: 2px 6px;
  background: #cc7801;
  color: #fff;
}
#stdout-container,
#stdin-container {
  height: auto;
  padding: 6px 0;
}
#reset {
  float: right;
}
#source-display-wrapper {
  display: none;
  width: 100%;
  height: 100%;
  overflow: auto;
  border: 1px solid black;
  box-sizing: border-box;
}
#source-display {
  font-family: monospace;
  white-space: pre;
  padding: 2px;
}
.activeToken {
  background: #f88912;
}
.clearfix:after {
  content: ".";
  display: block;
  height: 0;
  clear: both;
  visibility: hidden;
}
.clearfix {
  display: inline-block;
}
* html .clearfix {
  height: 1%;
}
.clearfix {
  display: block;
}
<!--
Designed and written 2015 by D. Loscutoff
Much of the HTML and CSS was taken from this Befunge interpreter by Ingo Bürk: http://codegolf.stackexchange.com/a/40331/16766
-->
<div class="container">
  <textarea id="source" placeholder="Enter your program here" wrap="off">i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!4*(4Sn1(</textarea>
  <div id="source-display-wrapper">
    <div id="source-display"></div>
  </div>
</div>
<div id="stdin-container" class="container">
  <textarea id="stdin" placeholder="Input" wrap="off">5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8</textarea>
</div>
<div id="controls-container" class="container clearfix">
  <input type="button" id="run" class="control so-box" value="Run" onclick="runBtnClick()" />
  <input type="button" id="pause" class="control so-box" value="Pause" onclick="program.halt()" />
  <input type="button" id="step" class="control so-box" value="Step" onclick="stepBtnClick()" />
  <input type="button" id="reset" class="control so-box" value="Reset" onclick="resetProgram()" />
</div>
<div id="stdout-container" class="container">
  <textarea id="stdout" placeholder="Output" wrap="off" readonly></textarea>
</div>
<div id="options-container" class="container">
  <div class="option so-box">Steps per Second: <span id="steps-per-second" contenteditable>1000</span>
  </div>
</div>

\$\endgroup\$
2
\$\begingroup\$

JavaScript ES6, 71

As an anonymous function

n=>[...n].map(x=>t+=~(y='q   rnb p PBN R   Q'.search(x))?y-9|1:0,t=0)|t
\$\endgroup\$
2
\$\begingroup\$

Perl 5, 71 63 bytes

%a=(P,1,N,3,B,3,R,5,Q,9);$\+=$a{$_}||-$a{uc$_}for<>=~/./g;print

This modifies $\ (the line separator for print, which starts false) for every alphanumeric in the string that's a key of the hash %a defined at the beginning. It increments $\ by the value of the hash if the letter is a key as is; otherwise, it increments by negative the value of the hash if the uppercased letter is a key; otherwise it adds nothing.

Many thanks to primo for saving me eight bytes (in a comment on this answer).


I can save another byte with another suggestion from primo (thanks!): change $a{$_}||-$a{uc$_} to $a{$_}-$a{$"^$_}. But that's a rather different answer than mine, I think, so I'll not take the "credit" (of −1 byte) for it.

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

Clojure/ClojureScript, 63 chars

#(apply +(map{"P"1"N"3"B"3"R"5"Q"9"p"-1"n"-3"b"-3"r"-5"q"-9}%))

Written using a ClojureScript REPL, should also be valid Clojure. Try it here. Enter it, then call it using (*1 "FEN_string_here")

Pretty straightforward. {"P"1..."q"-9} is a data structure literal for a map of "P" to 1, "N" to 3, etc. map takes the function as the first argument and the data structure to process as the second--in this case, it's using the feature that a data structure (the map literal) can act as its own accessor function. The string parameter (% from the function macro) can be treated as a list of individual character strings. Any character not in the map will end up as nil in the resulting list, which + happily ignores.

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

Pyth, 25 bytes

-Fmsmhy/4@S5%Ck12@Gd_rBz2

Demonstration

This uses the following mapping formula for letters in pbnrq, if k is the letter:

(4 / (((chr(k) % 12) % 5) + 1) * 2 + 1

This is represented in Pyth as:

hy/4@S5%Ck12

First, the program creates the case-swapped version of the input, then on both strings filters for lowercase letters, then applies the above formula, then sums and subtracts the black values from the white values.

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

Python 3, 93

v=dict(zip('pbnrqPBNRQ',[1,3,3,5,9]*2))
print(sum(v.get(c,0)*(-1)**(c>'Z')for c in input()))
\$\endgroup\$

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.