7
\$\begingroup\$

James Bond is about to start a new mission and you are his computer scientist friend. As Tic Tac Toe is becoming more and more popular you decide that nothing would be more underhanded than a Tic Tac Toe encoder/decoder™.

For each line the decoder will follow the following steps:

  • Convert from 'O','X' and ' ' [space] to trinary

    • ' ' -> 0

    • 'O' -> 1

    • 'X' -> 2

  • Convert from trinary to decimal

  • Convert from decimal to letter

Example: "XO "

  • 210

  • 21

  • v

The encoder will do the reverse.

As code is much more precise than words, see the reference answer below in which I give an example (very long) implementation of the problem in Python:

Your decoder should be able to decode all the messages encoded with my encoder correctly and my decoder should be able to decode all the messages encoded with your encoder.

This is code-golf, the shortest wins.

Additional informations

  • 0 must be converted to a, the alphabet ends with a whitespace, so 26-> ' ' [whitespace]

  • "XXX" is the max, it translates to "222" that translates to 26.

  • As in Tic Tac Toe the max line lenght is 3.

  • X -> 2 means X should be replaced by 2

  • Encoder/Decoder

    • The encoder goes from plaintext to code.
    • The decoder goes from code to plaintext.
\$\endgroup\$
14
  • \$\begingroup\$ What happens when the result of the third step in encoding is grater than 26 ? \$\endgroup\$
    – Optimizer
    Commented Dec 10, 2014 at 19:19
  • \$\begingroup\$ @optimizer It can't be: "OOO" is the max, it translates to "222" that translates to 26. \$\endgroup\$
    – Caridorc
    Commented Dec 10, 2014 at 19:21
  • 1
    \$\begingroup\$ Also, should 1 mean a or 0 mean a ? because if 0 means a, then XXX, i.e. 26 means } \$\endgroup\$
    – Optimizer
    Commented Dec 10, 2014 at 19:37
  • 1
    \$\begingroup\$ @Optimizer 0 means a \$\endgroup\$
    – Caridorc
    Commented Dec 10, 2014 at 20:01
  • 2
    \$\begingroup\$ You have to put all these things in the questions as the spec. Do you believe that people will read the comments for sure ? \$\endgroup\$
    – Optimizer
    Commented Dec 10, 2014 at 20:08

13 Answers 13

5
\$\begingroup\$

BRAINFUCK, 393 bytes (encoder only)

Guys, we're making something for James Bond. Languages like javascript and C are way too readable! If you want something that the russians will never understand, brainfuck is the only option. It's surly not the shortest or fastest option, but no-one will understand it (and, let's be honest, everyone loves brainfuck)

The code has a couple of limitations though, It only encodes and only one character at a time. Also it doesn't manually stop, you have the stop it after it spits out the output. But hey, it works. This is the code:

>>>,>++++++++[<-------->-]<->+++<[>[->+>+<<]>[-<<-[>]>>>[<[-<->]<[>]>>[[-]>>+<]
>-<]<<]>>>+<<[-<<+>>]<<<]>>>>>[-<<<<<+>>>>>]<<<<[-<<+>>]+++<[>[->+>+<<]>[-<<-[
>]>>>[<[-<->]<[>]>>[[-]>>+<]>-<]<<]>>>+<<[-<<+>>]<<<]>>>>>[-<<<<<+>>>>>]<<<<<<
[->>>+<<<]+[[-]>[-<+<+<+>>>]<[>++++++++++++++++++++++++++++++++++++++++++++++
++++++<[-]]<-[>> ++++++++++++++++++++++++++++++++++++<<[-]]<--[>>>---<<<[-]]>>>.]

If you want to try it: just run the code with one capital letter as input.

If you want it commented just ask me nicely. A lot of things can be done better but I don't feel like optimizing it now (because brainfuck). Maybe I'll make a decoder too as reversing the process probably isn't that difficult.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Bit late to the party, but pretty please comment it? \$\endgroup\$
    – aaaaaaa
    Commented Jun 28, 2016 at 6:22
4
\$\begingroup\$

C++, 168 + 135 = 303 bytes

EDIT: saved one byte by requiring uppercase input

I like doing these in C++ because I get to do all sorts of fun nastiness I would never ever do in C++ code.

Encoder (168):

Takes a string of uppercase letters and spaces as an argument.

#include<cstdio>
int main(int h,char**c){c[0][3]=0;for(c++;h=**c;c[0]++){h=h&32?26:h-65;for(int d=2,e;d+1;d--){h=(h-(e=h%3))/3;c[-1][d]=e&2?88:e*47+32;}printf(c[-1]);}}

Readable:

#include<cstdio>
int main(int h,char**c)
{
    c[0][3]=0;
    for(c++;h=**c;c[0]++)
    {
        h=h&32?26:h-65;
        for(int d=2,e;d+1;d--)
        {
            h=(h-(e=h%3))/3;
            c[-1][d]=e&2?88:e*47+32;
        }
        printf(c[-1]);
    }
}

Decoder (135):

Takes a string of X, O, and space as an argument.

#include<cstdio>
int b=9,v;int main(int h,char**c){for(c++;h=**c;c[0]++){v+=(h&16?2:h&1)*b;b/=3;if(!b)putchar(v==26?32:v+97),v=0,b=9;}}

Readable:

#include<cstdio>
int b=9,v;
int main(int h,char**c)
{
    for(c++;h=**c;c[0]++)
    {
        v+=(h&16?2:h&1)*b;
        b/=3;
        if(!b)putchar(v==26?32:v+97),v=0,b=9;
    }
}
\$\endgroup\$
1
3
\$\begingroup\$

CJam, 24 + 25 = 49 bytes

Encoder

qN/{:i40f/3b'a+_'{=S@?}%

Decoder

q{_Sc=26@'a-?Zb" OX"f=N}%

Try it online here

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

Javascript, ES6, 157 chars

Only encoder, 83

f=s=>s=="XXX"?" ":(parseInt(s.replace(/./g,x=>" OX".indexOf(x)),3)+10).toString(36)

Only decoder, 77

g=s=>(parseInt(10+s,36)-10).toString(3).substr(-3).replace(/./g,x=>" OX"[x])

Both, 157

p=parseInt;f=s=>s=="XXX"?" ":(p(s.replace(/./g,x=>" OX".indexOf(x)),3)+10).toString(36);g=s=>(p(10+s,36)-10).toString(3).substr(-3).replace(/./g,x=>" OX"[x])

Test

console.log(x=["   ", "XO ", "XXX"], x=x.map(f), x.map(g))


PS: Tried to perform some optimization, but failed: resulting code was longer then just concatenation. I don't publish it as it also contains a bug, which had already been fixed.

\$\endgroup\$
4
  • \$\begingroup\$ Your encoder/decoder fail for space/XXX \$\endgroup\$
    – Optimizer
    Commented Dec 10, 2014 at 20:23
  • \$\begingroup\$ @Optimizer I've fixed this bug. I have to read the task more carefully before posting an answer... \$\endgroup\$
    – Qwertiy
    Commented Dec 10, 2014 at 20:45
  • 3
    \$\begingroup\$ Not your fault. The spec kept on revealing itself in parts. \$\endgroup\$
    – Optimizer
    Commented Dec 10, 2014 at 20:49
  • \$\begingroup\$ Maybe my stupid actions will inspire you to improve something else: n=[36,3];r=s=>s.replace(/./g,x=>" OX"[x]||" OX".indexOf(x));f=(s,t,p=a=>(parseInt(a,n[1-t])+10-20*t).toString(n[t]))=>t?r(p(10+s).substr(-3)):s=="XXX"?" ":p(r(s)) usage: console.log(x=[" ", "XO ", "XXX"], x=x.map(z=>f(z,0)), x.map(z=>f(z,1))) \$\endgroup\$ Commented Sep 15, 2018 at 23:24
2
\$\begingroup\$

R, 121 + 115 = 236

I think I got the spec correct

Decoder Function 121

d=function(s){i=rev(as.integer(unlist(strsplit(chartr('XO ','210',s),''))));c(letters,' ')[sum(3^(0:(length(i)-1))*i)+1]}

Encoder Function 115

e=function(s){i=which(c(letters,' ')==s)-1;paste(chartr("210","XO ",c(i%/%9%%3,i%/%3%%3,i%%3)),sep='',collapse='')}

This only works on lower case characters. Is that a requirement?

Quick Test

> mapply(d,c("OO "," OO"," OO","X O","XXX"))
OO   OO  OO X O XXX 
"m" "e" "e" "t" " " 
> mapply(e,unlist(strsplit('meet ','')))
    m     e     e     t       
"OO " " OO" " OO" "X O" "XXX" 
\$\endgroup\$
1
2
+300
\$\begingroup\$

Go, 372 bytes \$= 29 + 171 + 170\$

  • Imports = 29 bytes
  • Decoder = 171 bytes
  • Encoder = 170 bytes
import(."strings";."strconv")
func d(s string)(k string){for _,l:=range Split(s,"\n"){i,_:=ParseInt(NewReplacer(" ","0","O","1","X","2").Replace(l),3,64)
k+=string('A'+i)}
return ReplaceAll(k,"["," ")}
func e(s string)(k string){q:=ReplaceAll(s," ","[")
for _,r:=range q{k+=NewReplacer("0"," ","1","O","2","X").Replace(FormatInt(int64(r-'A'),3))+"\n"}
return Trim(k,"\n")}

Attempt This Online!

Explanation

Decoder

import(."strings";."strconv")
func d(s string)(k string){
for _,l:=range Split(s,"\n"){            // for each line...
i,_:=ParseInt(                           // turn the string into an int
    NewReplacer(" ","0","O","1","X","2") // define the replacements
    .Replace(l),                         // replace the chars
    3,64)                                // turn it into trinary
k+=string('A'+i)}                        // append to the output string
return ReplaceAll(k,"["," ")}            // replace `[` with spaces

Encoder

import(."strings";."strconv")
func e(s string)(k string){
q:=ReplaceAll(s," ","[")                // replace `[` with spaces 
for _,r:=range q{                       // for each character...
k+=NewReplacer("0"," ","1","O","2","X") // define the replacements...
    .Replace(                           // and apply onto...
    FormatInt(int64(r-'A'),3))          // the character, turned into trinary
    +"\n"}                              // and add a newline
return Trim(k,"\n")}                    // trim the final newline
\$\endgroup\$
1
\$\begingroup\$

Excel, 81 + 87 = 168 bytes

Encoder:

=CONCAT(SWITCH(0+MID(BASE(TEXT(CODE(A1)-97,"[=63]26"),3,3),{1,2,3},1),2,"X",1,"O"," "))

Decoder:

=CHAR(97+TEXT(MMULT(SWITCH(MID(A1,{1,2,3},1),"X",2,"O",1,),3^{2;1;0}),"[=26]63"))

Input in cell A1 in both cases.

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

Nibbles, 12 + 14 = 26 bytes

Encoder, 12 bytes

.@.`@3%-$6 91=$"OX "

Attempt This Online!

-1 byte thanks to Dominic van Essen

.@.`@3%-%$32~27=$"OX "
.                      For each character c in
 @                      the input as a string
  .                     for each digit d in
   `@3                   convert to base 3
        %$32              ord(c) mod 32
       -    ~             minus 1
      %      27           mod 27
               =$        take the d-th character (1-indexed, wrapping)
                 "OX "    from "OX "

Decoder, 14 bytes

*" "`%.;@+`@3.$%/$10 3'a'"{"

Attempt This Online!

*" "`%.;@+`@3.$%/$10 3'a'"{"
*" "                         Join by spaces
    `%                   "{"  split on '{' characters
      .                        for each line l in
       ;@                       the input as a list of lines
         +            'a'        add the character code of 'a' to
          `@3                     convert from base 3
             .                     for each character c in
              $                     l
                /$10                ord(c) divided by 10
               %     3              modulo 3

I realized later that I could've simply used ?"OX"$ instead of %/$10 3. The byte count is equal.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ 12 bytes for the encoder using minus-6-modulo-91... \$\endgroup\$ Commented Jul 26, 2023 at 13:54
  • \$\begingroup\$ @DominicvanEssen Thanks! \$\endgroup\$
    – xigoi
    Commented Jul 26, 2023 at 14:26
0
\$\begingroup\$

Reference answer, completely ungolfed:

# Tic-Tac-Toe code encoder

def from_letter_to_decimal(letter):
    alphabet = "abcdefghijklmnopqrstuvwxyz "
    return alphabet.index(letter)

def from_decimal_to_trinary(decimal):
    digits = []
    while decimal:
        digits.append(str(decimal % 3))
        decimal //= 3
    return ''.join(list(reversed((digits))))

def from_trinary_to_line(trinary):
    replace_dict = {"0": " ",
                  "1": "O",
                  "2": "X"}
    for char in replace_dict:
        trinary = trinary.replace(char,replace_dict[char])
    return trinary


def encode_letter(letter):
    decimal = from_letter_to_decimal(letter)
    trinary = from_decimal_to_trinary(decimal)
    line = from_trinary_to_line(trinary)
    return line

def encode_text(text):
    return '\n'.join([encode_letter(letter) for letter in text])

print(encode_text("meet me under the red bridge"))

# Tic-Tac-Toe code decoder

boards = """
OO 
OO
OO
X O
XXX
OO 
OO
XXX
X X
OOO
O 
OO
OXX
XXX
X O
XO
OO
XXX
OXX
OO
O 
XXX
O
OXX
XX
O 
X 
OO

"""


def from_line_to_trinary(line):
    replace_dict = {" ": "0",
                  "O": "1",
                  "X": "2"}
    for char in replace_dict:
        line = line.replace(char,replace_dict[char])
    return line


def from_trinary_to_decimal(n):
    result = 0
    for position,i in enumerate(reversed(n)):
        result += int(i) * 3**position
    return result

def from_decimal_to_letter(n):
    alphabet = "abcdefghijklmnopqrstuvwxyz "
    return alphabet[n]

def decode_line(line):
    trinary = from_line_to_trinary(line)
    decimal = from_trinary_to_decimal(trinary)
    letter = from_decimal_to_letter(decimal)
    return letter

def decode_boards(boards):
    return ''.join([decode_line(line) for line in boards.splitlines() if not line==""])

print(decode_boards(boards))
\$\endgroup\$
3
  • \$\begingroup\$ I think you lost the leading spaces in the boards string in your decoder. \$\endgroup\$
    – marinus
    Commented Dec 10, 2014 at 19:41
  • \$\begingroup\$ @optimizer: over here the first few lines show up as OO_ OO OO X_O, which would be mmmt. \$\endgroup\$
    – marinus
    Commented Dec 10, 2014 at 19:48
  • \$\begingroup\$ @optimizer: oh wait, they're supposed to be right-aligned. duh. \$\endgroup\$
    – marinus
    Commented Dec 10, 2014 at 19:49
0
\$\begingroup\$

Ruby, 44 + 54 = 98 bytes

Encoder

->s{[*?A..?Z," "][s.tr(" OX","012").to_i 3]}

Attempt This Online!

Decoder

->c{((c<?A??[:c).ord-65).digits(3).join.tr"012"," OX"}

Attempt This Online!

\$\endgroup\$
0
\$\begingroup\$

Japt v1.4.5, 12 + 17 = 29 bytes

Encoder

;C+S gUn" OX

Try it

Decoder

;C+S aU s" OX" ù3

Try it

I tried some options that worked with codepoints rather than indexing into C+S, but they always ended up being longer in order to handle the space.

Explanations:

;C+S gUn" OX
;            # C = "abcdefghijklmnopqrstuvwxyz"
 C+S         # Add a space to the end of C
     g       # Get the character at index:
      U      #  Input string
       n" OX #  Converted to base 10 from base " OX"

;C+S aU s" OX" ù3
;                 # C = "abcdefghijklmnopqrstuvwxyz"
 C+S              # Add a space to the end of C
     aU           # Find the index of the input string
        s" OX"    # Convert to base " OX"
               ù3 # Left pad with spaces to length 3
\$\endgroup\$
0
\$\begingroup\$

Scala, 90+107=197 bytes

Port of @Jordan's Ruby answer in Scala.


Encoder

Golfed version. Attempt This Online!

s=>(('A'to'Z')++Seq(' '))(Integer.parseInt(s.map{case'O'=>'1'case'X'=>'2'case' '=>'0'},3))

Ungolfed version. Attempt This Online!

object Main {
  def main(args: Array[String]): Unit = {
    def f(s: String): Char = {
      val alphabet = ('A' to 'Z') ++ Seq(' ')
      val mapped = s.map {
        case 'O' => '1'
        case 'X' => '2'
        case ' ' => '0'
      }
      val idx = Integer.parseInt(mapped, 3)
      alphabet(idx)
    }

    println("["++f("XO ").toString++"]")
    println("["++f("XXX").toString++"]")
  }
}

Decoder

Golfed version. Attempt This Online!

c=>{val n=if(c<'A')26 else c-'A';Integer.toString(n,3).map{case'0'=>' ';case'1'=>'O';case'2'=>'X'}.reverse}

Ungolfed version. Attempt This Online!

object Main {
  def F(c: Char): String = {
    val n = if (c < 'A') 26 else (c - 'A').toInt
    val base3 = Integer.toString(n, 3)
    base3.map {
      case '0' => ' '
      case '1' => 'O'
      case '2' => 'X'
    }.reverse
  }

  def main(args: Array[String]): Unit = {
    println(F('V'))
    println(F(' '))
  }
}

\$\endgroup\$
0
\$\begingroup\$

APL (Dyalog Unicode), 18 + 18 = 36 bytes

Decoder:

(⊂3⊥⍉' O'⍳⎕)⌷27↑⎕A

Encoder:

' OX'[⍉3 3 3⊤⎕A⍳⎕]

Try it online!

\$\endgroup\$

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.