14
\$\begingroup\$

Scores

This section will be filled in as submissions are entered.

Normal

1. bopjesvla    Perl                54
2. edc65        Javascript (ES6)    91
3. name         language            score
4. name         language            score
5. name         language            score

Bonus Round

1. name   language   score
2. name   language   score
3. name   language   score
4. name   language   score
5. name   language   score

Karel J. AlphaBot

Background

A popular introductory course to Java is Karel J. Robot (I'm using it myself). The robot interacts with a grid of streets (positive integer y-coordinates) and avenues (positive integer x-coordinates) as well as beepers, which can be placed and stored on the grid (note that Karel and any beepers can only exist on lattice points). Karel (the robot) is only to perform five actions: move forward by 1, turn left in place, put down a beeper, pick up a beeper, and turn itself off.

In my Computer Science class, one of our first assignments was to program Karel to learn how to turn right, turn around, and perform the combined action of moving forward by 1 and putting down a beeper. An assignment a few days later was to use these methods and write new methods to produce letters of the alphabet.

Naturally, once I finished this assignment, I wrote more methods to make every letter of the alphabet, as well as the ten numerical digits, and I plan to figure out how to make a sort of word processor out of the robot, where a string would be entered to STDIN and the robot would put beepers on the grid in a way that resembled the letters.

Every time I wrote private void draw# for each character #, I added a comment after it that would tell me abbreviations for the sequence of commands I'd need.

I have the following commands (written in pseudocode) at my disposal (clarification - these are the only useful commands).

Turn Left
    Rotate the robot 90˚ counterclockwise
    Abbreviated as "l"

Turn Right
    Rotate the robot 90˚ clockwise
    Abbreviated as "r"

Move
    Move one space forwards
    Abbreviated as "m"

Put Beeper
    Put a beeper on the spot that Karel is on
    Abbreviated as "p"

Drop Beeper
    Move, then Put Beeper
    Abbreviated as "d"

Turn Around
    Turn Left, then Turn Left
    Abbreviated as "a"

Conditions

The robot must proceed in the following order.

  • The robot starts on the bottom left corner of the 5xN rectangle of minimal area the letter will be drawn in.
  • The robot draws the letter.
  • The robot moves to the bottom right corner of the rectangle.
  • The robot moves two spaces to the right and must face north/up

Let's work through an example. Suppose we want to draw A. The location of the robot is the letter that indicates its direction (north, south, east, west). The letter is capitalized if the robot is on a spot with a beeper and lowercase if the robot is on a spot without a beeper. o represents spots with beepers and . represents spots without beepers.

As we will see later, A is this.

.ooo.
o...o
ooooo
o...o
o...o

Here is one possible solution.

Grids   .....   .....   .....   .....   .....   .....   .....   .....   .....
        .....   .....   .....   .....   .....   .....   .....   .....   .....
        .....   .....   .....   N....   E....   oE...   ooE..   oooE.   oooW.
        .....   .....   N....   o....   o....   o....   o....   o....   o....
        n....   N....   o....   o....   o....   o....   o....   o....   o....

Letters           p       d       d       r       d       d       d       a

        .....   .....   .....   .....   .....   n....   e....   .E...   .oE..
        .....   .....   .....   .....   N....   o....   o....   o....   o....
        ooWo.   oWoo.   Wooo.   Nooo.   oooo.   oooo.   oooo.   oooo.   oooo.
        o....   o....   o....   o....   o....   o....   o....   o....   o....
        o....   o....   o....   o....   o....   o....   o....   o....   o....

          m       m       m       r       d       m       r       d       d

        .ooE.   .oooe   .ooos   .ooo.   .ooo.   .ooo.   .ooo.   .ooo.
        o....   o....   o....   o...S   o...o   o...o   o...o   o...o
        oooo.   oooo.   oooo.   oooo.   ooooS   ooooo   ooooo   ooooo
        o....   o....   o....   o....   o....   o...S   o...o   o...o
        o....   o....   o....   o....   o....   o....   o...S   o...E

          d       m       r       d       d       d       d       l

The final mml to complete the fourth bullet is implicit because it appears in every letter and because I don't want to go back and add another two columns to everything in the above proposed solution.

Thus, one solution to make A is pddrdddammmrdmrdddmrddddlmml.

Note that this doesn't have to be your solution. Your algorithm can go through every column, putting beepers in the proper places and not relying on where other beepers have been placed or will be placed. No matter what your algorithm is, the robot can only place one beeper per space on the grid.


The program

Your program will take as its input a 5xN grid of what the grid for the letter is. Note that there is no robot on the input; the robot is assumed to be on the bottom left (southwest) corner, facing north.

The output will be the sequence of letters that are the shorthand for the sequence.

Sample inputs

.ooo.
o...o
ooooo
o...o
o...o

o...o.ooooo
o...o...o..
ooooo...o..
o...o...o..
o...o.ooooo

Sample outputs

pddrdddammmrdmrdddmrddddlmml

prmmmlmlmmdrdrdddlmlmmdrdrmmmdrddddlmmlprdddlmldmmrmrmdmlmldmmrdrddddrmmmdlmml

This is code golf, fellas. Standard CG rules apply. Shortest code in bytes wins.


Bonus Round

Rules

If you want to participate in the bonus round, be sure to make your codes move-efficient! Below is a library of all of the 5x5 letters my program creates when it runs. The objective of the bonus round is to write a program that prints a sequence for ABCDEFGHIJKLMNOPQRSTUVWXYZ that contains as few moves as possible. There is no input to STDIN. Code will be graded not on the length of the code but on its "move score." The move score is designed to discourage sweeper algorithms that visit every point in the rectangle.

d: 1
l: 1
m: 4
p: 1
r: 1

Letters

.ooo.   oooo.   ooooo   oooo.   ooooo   ooooo   .oooo   o...o
o...o   o...o   o....   o...o   o....   o....   o....   o...o
ooooo   oooo.   o....   o...o   oooo    oooo.   o.ooo   ooooo
o...o   o...o   o....   o...o   o....   o....   o...o   o...o
o...o   oooo.   ooooo   oooo.   ooooo   o....   oooo.   o...o

ooooo   ....o   o...o   o....   ooooo   o...o   ooooo   oooo.
..o..   ....o   o..o.   o....   o.o.o   oo..o   o...o   o...o
..o..   ....o   oo...   o....   o.o.o   o.o.o   o...o   oooo.
..o..   o...o   o..o.   o....   o...o   o..oo   o...o   o....
ooooo   .ooo.   o...o   ooooo   o...o   o...o   ooooo   o....

oooo.   oooo.   ooooo   ooooo   o...o   o...o   o...o   o...o
o..o.   o...o   o....   ..o..   o...o   o...o   o...o   .o.o.
o..o.   oooo.   ooooo   ..o..   o...o   .o.o.   o.o.o   ..o..
oooo.   o..o.   ....o   ..o..   o...o   .o.o.   o.o.o   .o.o.
....o   o...o   ooooo   ..o..   ooooo   ..o..   ooooo   o...o

o...o   ooooo
.o.o.   ...o.
..o..   ..o..
.o...   .o...
o....   ooooo

The same procedure as the original challenge must be followed: letters must be drawn one at a time with a space separation between each letter.

Standard CG rules apply. Entry with the lowest move score wins.




To summarize, both codes will essentially do the same things. The first code should have a minimal number of bytes in the code, and the second code should use the smallest number of moves.

\$\endgroup\$
18
  • \$\begingroup\$ Neat challenge - no idea why you're getting downvoted. \$\endgroup\$ – Deusovi Sep 24 '15 at 4:44
  • 1
    \$\begingroup\$ Thanks @Deusovi. I wish they'd explain why so I can clear up anything that doesn't make sense or improve it. \$\endgroup\$ – Arcturus Sep 24 '15 at 11:38
  • \$\begingroup\$ "Karel (the robot) is only to perform five actions": I think you're missing "able", and you're definitely missing the fifth action. And I'm not sure what the bonus round is about: are you going to award a bounty to the person who writes the best solution? \$\endgroup\$ – Peter Taylor Sep 24 '15 at 13:35
  • \$\begingroup\$ Perhaps instead of a code golf challenge change it to a minimal move golf challenge? Since this is about efficiency. \$\endgroup\$ – LukStorms Sep 24 '15 at 13:50
  • 1
    \$\begingroup\$ A minimal move challenge with a limited set of moves isn't that interesting without the code golf part. It should be pretty easy to BFS the optimal path. \$\endgroup\$ – bopjesvla Sep 24 '15 at 16:51
5
\$\begingroup\$

perl -p0, 60 56 54+2 bytes

golf

/
/;$:="m"x"@-";$_=mmmmlma.s/
/rmr$:a/gr.mml;y/.o/md/;

notes

/\n/; # capture the length of the first line
$:="m"x"@-"; # assign a string of m's with that length to $:
s/^/mmmmlmll/; # move to the starting position (-1,0)
s/\n/rmr$:rr/g; # replace all newlines with kareliage returns
y/.o/md/; # replace dots with moves and o's with drops
s/$/mml/; # append mml
\$\endgroup\$
1
2
+50
\$\begingroup\$

JavaScript (ES6), 91

A first try to the basic challenge.

Test running the snippet below in an EcmaScript 6 compliant browser (tested in Firefox)

// BASIC CHALLENGE ANSWER

f=s=>`mmmmr${[...s].map(c=>c<' '?[`mrmr${b}a`,b=m][0]:(b+=m,c>m?'pm':m),b=m='m').join``}ml`

/* ALSO ...
f=s=>`mmmmr${s.replace(/.|\s/g,c=>c<' '?[`mrmr${b}a`,b=m][0]:(b+=m,c>m?'pm':m),b=m='m')}ml`
*/


// TEST
// now, I need to implement Karel (and test it) to do a significant test
// (a super limited version of Karel, of course. Just what is needed)

Karel=(cmd, width, height) =>  // even if for this test we have a limited height to handle
{ 
  var grid = [...('.'.repeat(width)+'\n').repeat(height)],
  o = width+1,
  p = o*(height-2)+1,
  d = [-o, 1, o, -1], // direction offsets
  q = 0, // face up
  s = [...grid],    
  steps = [];

  s[p] = 'n';
  steps.push([s.join``,'-']);
  
  [...cmd].forEach(c => 
    (
      c == 'l' ? q = q-1 &3
      : c == 'r' ? q = q+1 &3
      : c == 'a' ? q = q+2 &3
      : c == 'm' ? p += d[q]
      : c == 'p' ? grid[p] = 'o'
      : c == 'd' ? grid[p += d[q]] = 'o'
      : 0,
      s = [...grid],  
      s[p] = s[p] == 'o' ? 'NESW'[q] : 'nesw'[q],
      steps.push([s.join``,c])
    )
  )
  return [s.join``,steps]
}  
function go(cmd)
{
  var result = Karel(cmd,260,7)
  RESULT.innerHTML = result[0];
  STEPS.innerHTML = 'Steps:<br>'+result[1].map(x=>'<pre>'+x.join('\n')+'</pre>').join('')
}
function test(input)
{
  var exp = input.innerHTML;
  var cmd = f(exp);
  var result = Karel(cmd,20,7)
  CMD.innerHTML = cmd;
  RESULT.innerHTML = result[0];
  STEPS.innerHTML = 'Steps:<br>'+result[1].map(x=>'<pre>'+x.join('\n')+'</pre>').join('')
}
pre {
  float: left;
  border: 1px solid #888;
  margin: 2px
}

div {
  clear: both;
}

#CMD {
  word-break: break-all;
  white-space: normal;
  width:50%;
  overflow:auto;
}

#Y {
  width:50%;
}
Test cases (click to run a test)<br>
<pre id=T1 onclick='test(this)'>.ooo.
o...o
ooooo
o...o
o...o</pre>
<pre id=T2 onclick='test(this)'>o...o.ooooo
o...o...o..
ooooo...o..
o...o...o..
o...o.ooooo</pre>
<div>Command string:<br><pre id=CMD></pre></div>
<div>
Your Command:<br><input id=Y><button onclick='go(Y.value)'>-></button>
</div>  
<div>Result:<br><pre id=RESULT></pre></div>
<div id=STEPS>Steps</div>

BONUS CHALLENGE ANSWER - Score for full alphabet=869

Test running the wnippet below in Firefox (better full screen)

As I don't like fixed input/fixed output challenges, you can try your input. Just remember, only letters will be printed.

// Optimal - working on small pattern but too slow (scale bad)
// So I build the total command letter by letter - that surely is NOT globally optimal

Key=sol=>sol.pos+' '+sol.setBits

Solve=(target, startRow, startDir, cmd)=>{
  // Target is a rectangle string 5x5, newline separated for total (5+1)*5 chars
  if (target[target.length-1] != '\n') target += '\n';
  
  var T = ~new Date()
  var width = 5, height = 5, startPos = (width+1)*startRow;
  var offset = [-width-1, 1, width+1, -1];
  var turns = [ "", "r", "rr", "l" ];
  var cmds = [ "m", "rm", "rrm", "lm", "d", "rd", "rrd", "ld" ];
  var visited = {}, scan =[[],[],[],[],[],[],[],[]], cscan;
  
  var baseSol = { steps:[], pos: startPos, dir: startDir, setBits: 0};
  var score = 0, j = 0
  var bit, key, turn, curSol, move, result
  var targetBits = 0; 
  [...target].map((c,i)=>targetBits |= ((c=='o')<<i)) // 30 bits
  
  // First step, from outside, set bit in mask if it's set in target
  if (target[startPos]=='o') baseSol.setBits = 1<<startPos;
  console.log(target, targetBits.toString(16))
  visited[Key(baseSol)] = scan[0].push(baseSol);
  

  for (j = 0; j<99; j++)
  {
     cscan = scan[j];
     scan.push([])
     
     // console.log(`T: ${T-~new Date} J: ${j} SC: ${cscan.length}`)
     while (cscan.length > 0)
     {
        baseSol = cscan.pop()
        //console.log('Base', baseSol.dir, baseSol.pos, baseSol.setBits.toString(16), baseSol.steps.length)
        for(turn = 0; turn < 4; turn++)
        {
           // set direction, move and drop if you can
           curSol = {};
           curSol.dir = baseSol.dir + turn & 3;
           curSol.pos = baseSol.pos + offset[curSol.dir];
           // console.log(turn, curSol.dir, curSol.pos)
           if(target[curSol.pos] > ' '
              || curSol.dir == 1 && target[curSol.pos]=='\n'
             ) // if inside grid or on right border facing east
           {
              score = j + (turn == 2 ? 3 : turn == 0 ? 1 : 2);
              bit = 1 << curSol.pos;
              if (targetBits & bit)
                 curSol.setBits = baseSol.setBits | bit, move = 4 | turn;
              else
                 curSol.setBits = baseSol.setBits, score += 3, move = turn;
              if (!visited[key = Key(curSol)]) 
              {
                 curSol.steps = [...baseSol.steps, move] // clone and add
                 // task completed if on  right border and all bits ok
                 if (target[curSol.pos]>' ')
                 { // not on right border, proceed  
                    visited[key] = scan[score].push(curSol)
                 }  
                 else if (curSol.setBits == targetBits)
                 {
                    result = curSol.steps.map(v=>cmds[v]).join``
                    result = (cmd == '' 
                    ? target[startPos]=='o' ? 'p' : '' 
                    : target[startPos]=='o' ? 'd' : 'm') + result;
                    console.log(`T: ${T-~new Date} J: ${j} CMD: ${result}`)
                    return [cmd+result, curSol.pos / (width+1) | 0];
                 }
              }
           }
        }
     }
  }
  // Miserable failure!
  return []
}  

console.log=(...x)=>LOG.innerHTML+=x+'\n';
// TEST
Karel=(cmd, width, height) =>  // even if for this test we have a limited height to handle
{ 
  var grid = [...('.'.repeat(width)+'\n').repeat(height)],
  o = width+1,p = o*(height-2)+1,d = [-o, 1, o, -1], // direction offsets
  steps = [],s = [...grid],q = 0; // face up

  s[p] = 'n';
  steps.push([s.join``,'-']);
  
  [...cmd].forEach(c => 
    (
      c == 'l' ? q = q-1 &3
      : c == 'r' ? q = q+1 &3
      : c == 'a' ? q = q+2 &3
      : c == 'm' ? p += d[q]
      : c == 'p' ? grid[p] = 'o'
      : c == 'd' ? grid[p += d[q]] = 'o'
      : 0,
      s = [...grid],  
      s[p] = s[p] == 'o' ? 'NESW'[q] : 'nesw'[q],
      steps.push([s.join``,c])
    )
  )
  return [s.join``,steps]
}  


var AlphabetMap = `.ooo..oooo..ooooo.oooo..ooooo.ooooo..oooo.o...o.ooooo.....o.o...o.o.....ooooo.o...o.ooooo.oooo..oooo..oooo..ooooo.ooooo.o...o.o...o.o...o.o...o.o...o.ooooo
o...o.o...o.o.....o...o.o.....o.....o.....o...o...o.......o.o..o..o.....o.o.o.oo..o.o...o.o...o.o..o..o...o.o.......o...o...o.o...o.o...o..o.o...o.o.....o.
ooooo.oooo..o.....o...o.oooo..oooo..o.ooo.ooooo...o.......o.oo....o.....o.o.o.o.o.o.o...o.oooo..o..o..oooo..ooooo...o...o...o..o.o..o.o.o...o.....o.....o..
o...o.o...o.o.....o...o.o.....o.....o...o.o...o...o...o...o.o..o..o.....o...o.o..oo.o...o.o.....oooo..o..o......o...o...o...o..o.o..o.o.o..o.o...o.....o...
o...o.oooo..ooooo.oooo..ooooo.o.....oooo..o...o.ooooo..ooo..o...o.ooooo.o...o.o...o.ooooo.o.........o.o...o.ooooo...o...ooooo...o...ooooo.o...o.o.....ooooo`.split('\n')
var LetterMap = [];
var l,row,m;

for (l=0;l<26;l++)
{
  for(m='',row=0;row<5;row++)
    m += AlphabetMap[row].substr(l*6,5)+'\n'
  LetterMap[l]=m;  
}

print=Message=>{
  var Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  var startRow = 4, cmd=''
  var startDir = 0 // start facing UP
  ;[...Message].forEach(l => (
    [cmd, startRow] = Solve(LetterMap[Alphabet.search(l)], startRow, startDir, cmd),
    startDir = 1, // after each letter will be facing RIGHT
    cmd += '\n' // addin a newline (scoring 0) just for readability
  ))

  if (startRow != 4) 
    cmd += 'mr'+'m'.repeat(4-startRow)+'rr' // on last row and facing up
  else 
    cmd += 'ml' // ...facing up

  // Recalc score
  var score = 0
  ;[...cmd].forEach(c=>score += c=='m'? 4 : c<' '? 0: 1)

  var robot = Karel(cmd.replace(/\n/g,''), 26*7, 7)
  O.innerHTML=cmd+'\nScore:'+score
  R.innerHTML=robot[0]
  RS.innerHTML=robot[1].join`\n`
}  

function test()
{
  var msg = I.value.toUpperCase()
  msg=msg.replace(/[^A-Z]/g,'')
  I.value=msg
  print(I.value)
}

test()
fieldset {
  padding:0;
}

pre {
  margin: 2px;
}

#RS {
  height: 200px;
  width: 50%;
  overflow:auto;
}

#I { width: 50% }
<fieldset ><legend>Message to print</legend>
<input id=I value='ABCDEFGHIJKLMNOPQRSTUVWXYZ'><button onclick='test()'>go</button></fieldset>
<fieldset ><legend>Command Result (newlines added for readability)</legend>
<pre id=O></pre></fieldset>
<fieldset ><legend>Robot output</legend>
<pre id=R></pre></fieldset>
<fieldset ><legend>Robot step by step</legend>
<pre id=RS></pre></fieldset>
<fieldset ><legend>log</legend>
<pre id=LOG></pre></fieldset>

\$\endgroup\$
3
  • \$\begingroup\$ How's the bonus going? \$\endgroup\$ – Arcturus Oct 2 '15 at 23:51
  • \$\begingroup\$ @Eridan the bonus is going well. Unfortunately I have a job too ... :) \$\endgroup\$ – edc65 Oct 3 '15 at 17:47
  • \$\begingroup\$ Ok! I don't blame you. You're the only one who's attempted the bonus. \$\endgroup\$ – Arcturus Oct 3 '15 at 20:22

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.