Here is a tic-tac-toe board:

   a     b     c
      |     |     
1  -  |  -  |  -  
      |     |     
2  -  |  -  |  -  
      |     |     
3  -  |  -  |  -  
      |     |     

Given a set of moves, print the board with the tokens on.

Input will be taken in as moves separated by spaces, with each move being:

  • First, the token that's going
  • Next, the letter of the column it is moving on
  • Last, the number of the row it is moving on

Note that normal tic-tac-toe rules don't matter.

Also, there is no need to print the letters and numbers.

For example, the input Ob2 Xc2 Ob3 Xa1 would result in

     |     |     
  X  |  -  |  -  
     |     |     
  -  |  O  |  X  
     |     |     
  -  |  O  |  -  
     |     |     

Remember, this is , so the code with the smallest number of bytes wins.


Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes

var QUESTION_ID=95629,OVERRIDE_USER=12537;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

  • \$\begingroup\$ A thing to avoid with writing challenges is Cumbersome I/O formats. Please make your input format flexible enough. Next time try the Sandbox. \$\endgroup\$ Commented Oct 8, 2016 at 10:08
  • 1
    \$\begingroup\$ @EriktheGolfer cumbersome? This input format is plain and obvious \$\endgroup\$
    – edc65
    Commented Oct 8, 2016 at 12:45
  • \$\begingroup\$ @edc65 I meant that, I want to get input in any format, not just this one, but I guess it's late by now. I think you mean it's visually clear, but I couldn't handle it at all. Why spaces and not some other separator for example? \$\endgroup\$ Commented Oct 8, 2016 at 13:58
  • 1
    \$\begingroup\$ @EriktheGolfer don't take me wrong, but if your super extra esoteric language cannot handle blank spaces, too bad ... you'll have to use something else just this time. \$\endgroup\$
    – edc65
    Commented Oct 8, 2016 at 14:02
  • 1
    \$\begingroup\$ @edc65 For example, in Sesos it would be pretty difficult to even load the input data. I would prefer a list of integers (three ints for one move), like 1 2 3 for Xb3 (doesn't have to be exactly that). \$\endgroup\$ Commented Oct 8, 2016 at 14:06

10 Answers 10


JavaScript (ES6), 136 133 129 bytes

let f =

i=>eval("for(y=9,s='';y--;s+=`\n`)for(x=18;--x;)s+=x%6-3|y%3-1?' __|'[x%6?y/3:3]||' ':i[i.search('cba'[x/6|0]+(10-y)/3)-1]||'-'")

console.log(f("Ob2 Xc2 Ob3 Xa1"))


Python (2.7), 188 180 bytes

def f(s):a,b=3*[' '*5],3*['_'*5];c,d,e=[['  '+dict((m[1:],m[0])for m in s.split(' ')).get(x+y,'-')+'  'for x in'abc']for y in'123'];print'\n'.join(map('|'.join,[a,c,b,a,d,b,a,e,a]))
  • \$\begingroup\$ In the third line you can remove the spaces before the 's \$\endgroup\$
    – Daniel
    Commented Oct 8, 2016 at 2:09
  • \$\begingroup\$ Welcome to python golfing! Take a look at the Python golf tips. You can separate statements with ; allowing you to put all your code on one line to save on indentation. \$\endgroup\$
    – xnor
    Commented Oct 8, 2016 at 3:07
  • \$\begingroup\$ for y in '123' -> for y in'123'. Also, what xnor said: you can make your function a one-liner, using ; to separate statements. Using all of these golfing tips, you can golf 6 bytes. \$\endgroup\$ Commented Oct 8, 2016 at 9:55

Python 2, 112 bytes

s=input()+'- '
r=3;exec"print'|'.join([' '*5,'  %s  '%s[s.find(c+`r/3`)-1],'_'*5][r%11%3]for c in'abc');r+=1;"*9

Nine rows are printed corresponding to row indices r. The row index is counted up from 3 to 11 in an exec loop. Each row consists of vertical lines | joining three 5-character segments that cycle between:

  • Five spaces
  • Two spaces, a player symbol, then two spaces
  • Five underscores

The type is made to cycle with r%3, except the last row has spaces rather than underscores, achieved with r%11%3.

To find the player symbol for the current cell, we look at the row number r/3 and the column letter c from "abc". We concatenate them to make a two-character string like b3, find the index of it in the input string, and then take the symbol (X or O) one index earlier. If neither player played there, the find will default to -1, which decrements to -2. We hack s[-2] to be - by appending two characters when we take input.


PHP, 187 Bytes

<?for(;$i++<162;)$s.=$i%18?($i%6?($i<144&&$i%54>36?"_":($i<144&&$i%54>18&&$i%6==3?"-":" ")):"|"):"\n";foreach(explode(" ",$_GET[a])as$t)$s[20+6*(1+($t[1]<=>b))+54*($t[2]-1)]=$t[0];echo$s;

Takes the input as string. If I could use an array it can be reduce to $_GET[a] instead of explode(" ",$_GET[a])

207 Bytes

<?foreach(explode(" ",$_GET[a])as$t)$a[(1+($t[1]<=>b))+3*($t[2]-1)]=$t[0];for(;$i++<162;)$s.=$i%18?($i%6?($i<144&&$i%54>36?"_":" "):"|"):"\n";for(;$x<9;)$s[18+54*(($x/3)^0)+2+6*($x%3)]=$a[+$x++]??"-";echo$s;

The simplest idea to create this 194 Bytes

<?for(;++$i<10;)$s.=($i==9||$i%3?($i%3==2?"  -  |  -  |  -  ":"     |     |     "):"_____|_____|_____")."\n";foreach(explode(" ",$_GET[a])as$t)$s[20+6*(1+($t[1]<=>b))+54*($t[2]-1)]=$t[0];echo$s;

Mathematica, 205 bytes

StringReplacePart[a="     |     |     
";b="  -  |  -  |  -  

This would be way shorter if I could use the built-in... (92 bytes)


Java, 138 bytes


  • -2 bytes off. Thanks to @Kevin Kruijssen


m->{char[][]o=new char[3][3];for(char[]a:m)o[a[1]-'a'][a[2]-'0']=a[0];for(char[]a:o)System.out.println(a[0]+'|'+a[1]+'|'+a[2]+"\n-----");}


public static void tictactoe(char[][]moves){
  char[][]o=new char[3][3];
  • \$\begingroup\$ o[a[1]-'a'][a[2]-'0']=a[0] can be golfed by two bytes to o[a[1]-97][a[2]-48]=a[0] \$\endgroup\$ Commented Oct 10, 2016 at 11:53
  • \$\begingroup\$ but if's the wrong output, isn't it? \$\endgroup\$
    – norganos
    Commented Oct 11, 2016 at 5:33
  • \$\begingroup\$ Which mistake do you mean? \$\endgroup\$ Commented Oct 11, 2016 at 5:34
  • \$\begingroup\$ The output format is not the same as asked by the OP. \$\endgroup\$
    – seshoumara
    Commented Feb 21, 2017 at 19:32

Batch, 341 339 305 287 bytes

@for %%a in (a1 a2 a3 b1 b2 b3 c1 c2 c3)do @set %%a=-
@for %%a in (%*)do @set s=%%a&call call set %%s:~1%%=%%s:~0,1%%
@set s="     |     |     "
@for %%l in (%s% "  %a1%  |  %b1%  |  %c1%" %s: =_% %s% "  %a2%  |  %b2%  |  %c2%" %s: =_% %s% "  %a3%  |  %b3%  |  %c3%" %s%)do @echo %%~l

Assumes consistent output. Edit: Saved 2 bytes by removing unnecessary spaces. Saved 34 bytes by writing the output using a for loop. Saved 18 bytes by eliminating the subroutine.


Autovim, 110 bytes (not competing)

Test driving Autovim... This is pretty much a Vimscript answer. Not competing because Autovim is still being developed.

ñ5i ␛a|␛ÿp3hr-phv0r_⌥v$kkyPPy2jPP$⌥vG$xGđkÿjp

To run it:

./bin/autovim run tictactoe.autovim -ni -@m "Ob2 Xc2 Ob3 Xa1"


execute "normal 5i \<esc>a|\<esc>yyp3hr-phv0r_\<c-v>$kkyPPy2jPP$\<c-v>G$xGddkyyjp"
for @n in split(@m)
execute "normal gg0".(@n[2]*3-2)."j".((char2nr(@n[1])-96)*6-4)."lr".(@n[0]).""

Explanation to follow if there is interest :)


Groovy, 174 Bytes

{s->def r=0,o,t=[:];s.split(' ').each{t[it[1..2]]=it[0]};9.times{y->o='';17.times{x->o+=x%6==5?'|':y in [2,5]?'_':x%6==2&&y%3==1?t['abc'[r++%3]+(y+2)/3]?:'-':' '};println o}}


    def r=0, o, t=[:];
    s.split(' ').each{
            o+= x%6==5 ? '|' : y in [2,5]? '_' : x%6==2 && y%3==1 ? t['abc'[r++%3]+(y+2)/3]?:'-' : ' '
        println o

CJam, 62 bytes

" -_ -_ - "_'-Ser_+_@\]3*sqS/{)~\)'a-F*@+3*G+\t}/9/5/'|9*a*zN*

Try it online


" -_ -_ - "_'-Ser_+_@\]3*s e# Build a 135 character string representing the 
                           e# columns of the board (top to bottom, left to right)
qS/{)~\)'a-F*@+3*G+\t}/    e# Process the input, put the tokens (O,X) in the string
                           e# The tokens have to placed at indexes 
                           e# [19 22 25 64 67 70 109 112 115]
                           e# This is done with the formula 3*(15x+y)+16,
                           e# where x is the code point of the column letter 
                           e# (minus 'a') and y is the row number.
9/5/'|9*a*zN*              e# Split into its parts, add the column separators, zip 
                           e# and join with newlines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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