# Flag Semaphore Encoder

Your goal is to write a flag semaphore encoder, which will convert a given sentence into the corresponding flag semaphore characters, according to the semaphore system described on Wikipedia.

Assume that the input is a single sentence provided through stdin (or equivalent). Your output should be a series of semaphore characters, with each row representing one word from the sentence. You only need to deal with the alphabet (A-Z) and should ignore all other non-space characters, but you must be able to handle both uppercase and lowercase. Your output is allowed to contain extra whitespace.

Semaphore characters must be displayed as a 3x3 square, with an O in the middle and the flag positions represented by the characters | - / \. Each semaphore character must be separated from adjacent characters by a space, and each row must be separated by a blank line. Don't worry about wrapping for words that may be too long for your display - pretend that your lines have infinite length.

Sample input:

abcdefg hijklmn opqrstu vwxyz

Sample output:

\    |    /
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-
\       \       \

Sample input:

This is Code Golf.

Sample output:

\|      \
O  -O   O  -O
/   /     \

\
O  -O
/     \

\   \    |    /
O  -O   O   O
|       |   |

\     /
O  -O   O   O-
|\     /    |

Since this is , the shortest solution wins.

• kolmogorov-complexity? It seems to me that fundamentally this is about compressing a lookup table. Sep 2, 2011 at 17:07
• @Peter Taylor Yeah, it's probably a good idea to add that tag. Thanks. Sep 2, 2011 at 17:27
• and each row must be separated by a blank line => each word is meant, isn't it? Sep 2, 2011 at 20:53
• Before I read this puzzle, I mistakenly thought it had to do with semaphores in the programming sense. ¡Jajajajajja! Sep 2, 2011 at 21:44
• @user unknown I was using row there to refer to a row of semaphore characters. Perhaps using word instead would have been a better choice. Sep 2, 2011 at 23:34

## Perl, 282264251247245243241240236233229227220218216 214 characters

$_=lc<>;map{y/a-z//cd;y/a-z/HABDP\xc0(!\x12"$0\xa0\t\n\f\30\x88\3\5\x82\24\x84\21\x90/;@a=($/)x4;map{$s=ord;$a[$_/3].=substr" \\|/-O-/|\\",$_==4||$s>>$_-($_>4)&1?$_+1:0,1for 0..8;$_.=" "for@a}split//;print@a}split

With some prettifying line breaks:

$_=lc<>; map{ y/a-z//cd; y/a-z/HABDP\xc0(!\x12"$0\xa0\t\n\f\30\x88\3\5\x82\24\x84\21\x90/;
@a=($/)x4; map{$s=ord;
$a[$_/3].=substr" \\|/-O-/|\\",$_==4||$s>>$_-($_>4)&1?$_+1:0,1for 0..8;$_.=" "for@a
}split//;
print@a}split

It's taken me a while to get this working (my first attempt at a Perl answer). It's based on a similar idea to a lot of the other answers. Each flag can be in one of 8 positions, there are two flags, and the two flags can't ever be in the same position. This means I can encode the position of both flags in one byte - which also means I can translate directly from a character to its encoding using Perl's y/// function (operator?). So:-

a = 01100000 96 = 
b = 01001000 72 = H
c = 01000001 65 = A
d = 01000010 66 = B
e = 01000100 68 = D
f = 01010000 80 = P
etc...

Therefore:

y/a-z/HABDP..../;

I've escaped a fair few of the characters which are outside the normal used range to make the copying and pasting of the program easy - but I'm fairly sure I could write a program to replace the escape codes with the characters themselves saving me roughly 30 characters.

Python, 244 238 233 232

e='abhioptuwycdjmnsqxzfgvklebr'
for w in raw_input().split():
for i in 0,3,6,9:print' '.join(''.join((' '+'\|/-O-/|\ '[j])[jin'4'+'6736031025071568328578162735'[e.find(c):][:2]]for j in range(i,9)[:3])for c in w if c.lower()in e)

This uses a favorite trick of mine: single-track encoding. I've labeled the semaphore bits (sbits)

\|/     012
- -  -> 3 5
/|\     678

to obtain the following chart of which sbits occur for which letter:

0: ciotuy
1: djkptv
2: elquwx
3: bhopqrs
5: fjmrwyz
6: ahiklmn
7: abcdefg
8: gnsvxz

each letter occurs exactly twice in the chart, since the signalman has two arms. Then, I view this as a graph on the letters a-z, with edges between letters sharing sbits, with the edges labeled according to the shared sbit. Ideally, I'd find a Hamilton path through this graph, such that subsequent edges don't have the same label. No such paths exist... so you'll note that the variable e contains the letter b twice.

With my nearly-Hamilton path e, I construct an array d of sbit labels used in the traversal of e. Then, to figure out where to put her arms, the signalman need only find the desired letter in the following handy chart

abhioptuwycdjmnsqxzfgvklebr
6736031025071568328578162735

whence her arms go in the position directly below, and below&right of the letter.

• I couldn't get this to run on ideone until I changed to_lower() to lower(). Also, it gave an error when I tried giving it non-alphabetic input. Sep 2, 2011 at 23:42

### Scala, 272 characters

println(readLine.filter(c=>c.isLetter||c==' ').toLowerCase.split(" ").map{_.map(q=>("    O    "/:("^@a,6Tr?W*+5Sq9(2Pn%/-47MU"(q-'a')-27+""))((g,x)=>g.updated(x-'0',"\\|/-O-/|\\"(x-'0'))).grouped(3).toList).transpose.map(_.mkString(" ")).mkString("\n")}.mkString("\n\n"))

Ungolfed (well, less-golfed):

println(
readLine.filter(c => c.isLetter || c==' ').
toLowerCase.
split(" ").
map{ s =>
val lookup = "^@a,6Tr?W*+5Sq9(2Pn%/-47MU".map(c => (c-27).toString)
s.map(q =>
("    O    " /: lookup(q-'a')){(g,x) =>
g.updated(x-'0', "\\|/-O-/|\\"(x-'0'))
}.grouped(3).toList
).transpose.map(_.mkString(" ")).mkString("\n")
}.mkString("\n\n")
)

### Ruby, 287 characters

Input must be given on STDIN.

## Scala 494 without newlines 520 with newlines:

def k(i:Int,d:Int=0):(Int,Int)=if(i<(7-d))(d,i+1)else k(i-(7-d),d+1)
def t(i:Char)=(if(i=='y')i-4 else
if(i=='z')i+2 else
if(i=='j')i+14 else
if(i>='v')i+3 else
if(i>'i')i-1 else i)-'a'
def q(p:(Int,Int),i:Int,c:Char)=if(p._1==i||p._1+p._2==i)""+c else" "
def g(r:Int,c:Char)={val p=k(t(c.toLower))
print((r match{case 1=>q(p,3,'\\')+q(p,4,'|')+q(p,5,'/')
case 2=>q(p,2,'-')+"o"+q(p,6,'-')
case 3=>q(p,1,'/')+q(p,0,'|')+q(p,7,'\\')})+" ")}

### ungolfed:

def toClock (i: Int, depth: Int=0) : (Int, Int) = {
if (i < (7 - depth)) (depth, i+1) else toClock (i - (7-depth), depth + 1)}

def toIdx (i: Char) = {
(if (i == 'y') i - 4  else
if (i == 'z') i + 2  else
if (i == 'j') i + 14 else
if (i >= 'v') i + 3 else
if (i > 'i') i - 1 else i ) - 'a'}

def p2c (pair: (Int, Int), i: Int, c: Char) = {
if (pair._1 == i || pair._1 + pair._2 == i) ""+c else " "
}

def printGrid (row: Int, c: Char) = {
val idx = toIdx (c.toLower)
val pair = toClock (idx)
row match {
case 1 => { print(
p2c (pair, 3, '\\') +
p2c (pair, 4, '|') +
p2c (pair, 5, '/') + " ")
}
case 2 => { print(
p2c (pair, 2, '-') + "o" +
p2c (pair, 6, '-') + " ")
}
case 3 => { print(
p2c (pair, 1, '/') +
p2c (pair, 0, '|') +
p2c (pair, 7, '\\') + " ")
}
}
}

val worte = "This is Code Golf"
(1 to 3).map (row => {worte.map (c => printGrid (row, c));println})

### Explanation:

I observed a clock-pattern, but not with 12 hours, but 8. And Starttime is 0 is, where 6 o'clock is, and a, b, c are the first codes, with the first (one) flag on South.

Since flag 1 and 2 are indistinguishable, we can sort all combinations with the lower number for the first flag first. Unfortunately, the fine order from the beginning is disturbed, when j doesn't follow i, but k, l, m, and later it get's a mess.

Therefore I rearrange my keys for the mapping:

val iis = is.map {i =>
if (i == 'y') i - 4  else
if (i == 'z') i + 2  else
if (i == 'j') i + 14 else
if (i >= 'v') i + 3 else
if (i > 'i') i - 1 else i }.map (_ - 'a')

iis.zipWithIndex .sortBy (_._1) .map (p => (p._1, ('a' + p._2).toChar))

Vector((97,a), (98, b), (99, c), (100,d), (101,e), (102,f), (103,g),
(104,h), (105,i), (106,k), (107,l), (108,m), (109,n),
(110,o), (111,p), (112,q), (113,r), (114,s),
(115,t), (116,u), (117,y), -------
-------  (120,j), (121,v),
(122,w), (123,x),
(124,z))

If we substract 'a' from every character, we get the numbers from (0 to 7+6+5+...+1). We can map the numbers of a character-grid

3 4 5   \ | /            |
2   6   - o -    - o   - o
1 0 7   / | \    (2, ) (2,2)

A pair of two numbers can map two flags, where the first number is the index of 0 to 6 for the first flag, and the second flag isn't a number from 1 to 7 for the second flag, but for the distance from the first to the second flag. (2,2) would mean, the first flag is to WEST, and the second is two steps clockwise from there, to the NORTH.

def toClock (i: Int, depth: Int=0) : (Int, Int) = {
if (i < (7 - depth)) (depth, i+1) else toClock (i - (7-depth), depth + 1)}

Vector( (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7),
(1,1), (1,2), (1,3), (1,4), (1,5), (1,6),
(2,1), (2,2), (2,3), (2,4), (2,5),
(3,1), (3,2), (3,3),
(4,2), (4,3),
(5,1), (5,2),
(6,1))
• I don't know much about Scala. Is there a way I can test this on ideone? I tried wrapping it in an object Main extends Application block, but that doesn't seem to be enough. Sep 2, 2011 at 23:50
• IDEONE needs a class named Main, if I remember correctly, a main method, should maybe extend App (for scala-2.9, instead of Application (-2.8)) - and does it read from stdin? In simplyscala you may test code more simply. If you replace readLine in the last line with "readLine" it should work (the code is 2.8 compatible). Sep 3, 2011 at 0:11
• Thanks! I didn't know about simplyscala, that makes things much easier. Sep 3, 2011 at 0:48
• If you need the link again: I inserted the link somewhere in the meta already, where such things are collected. Sep 3, 2011 at 1:46
• Does this handle uppercase? Sep 3, 2011 at 9:14

Golfed:

import Data.Char
t[x,y]=q[x,mod(y+1)8]
q z@[x,y]|x==y=[x+1,y+2]|0<1=z
x%y=[x,y]
c 65=0%1
c 74=6%4
c 75=1%4
c 79=2%3
c 84=3%4
c 86=4%7
c 87=5%6
c 89=3%6
c 90=6%7
c x=t$c$pred x
_!9='O'
c!n|nelemc="|/-\\"!!mod n 4|0<1=' '
s x=do n<-[3:4%5,2:9%6,1:0%7];'\n':do c<-x;' ':map(c!)n
main=putStr.s.map(c.ord.toUpper)=<<getLine

Ungolfed:

type Clock = [Int]

tick :: Clock -> Clock
tick [h, m] = tick' [h, mod (m + 1) 8]

tick' :: Clock -> Clock
tick' [h, m]
| h == m = [h + 1, m + 2]
| otherwise = [h, m]

clock :: Char -> Clock
clock 'a' = [0,1]
clock 'j' = [6,4]
clock 'k' = [1,4]
clock 'o' = [2,3]
clock 't' = [3,4]
clock 'v' = [4,7]
clock 'w' = [5,6]
clock 'y' = [3,6]
clock 'z' = [6,7]
clock c = tick $clock$ pred c

arm :: Int -> Char
arm 0 = '|'
arm 1 = '/'
arm 2 = '-'
arm 3 = '\\'

drawAt :: Clock -> Int -> Char
drawAt _ 9 = 'O'
drawAt c n = if n elem c
then arm $n mod 4 else ' ' -- showClock is not in golfed code. Just there for debugging. showClock :: Clock -> String showClock c = unlines$ map (map $drawAt c) [ [3,4,5] , [2,9,6] , [1,0,7] ] showClocks :: [Clock] -> String showClocks cs = unlines$ map (showClocks' cs) [[3,4,5],[2,9,6],[1,0,7]]

showClocks' :: [Clock] -> [Int] -> String
showClocks' cs ns = cs >>= \c -> ' ' : map (drawAt c)

mainx :: IO ()
mainx = putStr . showClocks . map clock =<< getLine

345    \|/                                     \
2 6 == -O-          -O          tick  -O   ==   O      tick   O   ==  -O
107    /|\          /                 /        /              |\      /
[1,2] or [2,1]    tick [1,2] == [1,3]     tick [0,7] == [1,2]

Encoding is [hour, minute] where clocks have 8 hours and 8 minutes. Minutes move faster than hours. If a clock ticks where the hour and minute would be equal, add 1 to the hour and 2 to the minute as well (see second tick example above). That is the only way hours increase. Hours do NOT increase when the minute reaches some arbitrary minute. Only when minutes would equal hours. In the ungolfed code, clock turns letters into clocks that represent the semaphore. Most clocks are built based on ticking from previous ones. The rest are hard coded. There's nothing really more to the code.

Perl, 356, 275 chars

Great number of chars was saved by replacing 'if else' to '? :' construction.

@_=split('', $ARGV[0]);for (@_){print eval{/[ciotuy]/ ?'\\':' '}.eval{/[djkptv]/ ?'|':' '}.eval{/[elquwx]/ ?'/':' '}."\n".eval{/[bhopqrs]/ ?'-':' '}."0".eval{/[fjmrwyz]/ ?'-':' '}."\n".eval{/[ahiklmn]/ ?'/':' '}.eval{/[abcdefg ]/ ?'|':' '}.eval{/[gnsvxz]/ ?'\\':' '."\n"};} • Your code only seems to work for lower case letters. If you use <> instead of$ARGV[0] you can take input from stdin and use lc to convert all characters to lower case. This has the added benefit of saving you 4 characters. It doesn't handle non-alphabet characters either, but arguably You only need to deal with the alphabet (A-Z) and should ignore all other non-space characters isn't very clear on what to do with them... Sep 7, 2011 at 9:14
• I'm not able to test the code right now, but it looks like the code just prints spaces for non-alpha characters, which is fine. Sep 8, 2011 at 4:53
• @migimaru I`ll try to correct it.
– zura
Sep 10, 2011 at 23:46
• @zura Printing spaces for non-alphabet characters is acceptable. You don't need to fix that. Sep 11, 2011 at 2:11

# PowerShell, 198192191 188 bytes

-split$args|%{$s=$_ "\|/ ciotuy djkptv elquwx","-O- bho-s ^ fjmrwyz","/|\ ahik-n a-g gnsvxz"|%{$f,$p=-split$_
($s|% t*y|%{$c=$_ -join(&{$p|%{" $f"[++$i*($c-match"[$_ ]")]}})})-join' '}
''}

Try it online!

The output contains one tail empty line.

Less golfed:

-split$args|%{$string=$_ "\|/ ciotuy djkptv elquwx", "-O- bho-s ^ fjmrwyz", "/|\ ahik-n a-g gnsvxz"|%{$flags,$patterns=-split$_
$row=$string|% toCharArray|%{
$char=$_
$semaphoreRow=&{ # call the scriptblock in a new scope to reinit$i
$patterns|%{ "$flags"[++$i*($char-match"[$_ ]")] # return a flag symbol } } -join($semaphoreRow)
}
$row-join' ' } '' } # Charcoal, 70 bytes Ｆ⪪↧Ｓ «Ｆι«Ｆ⪪”↶↖→∧gτ→|⮌!⧴KD✂‖5»⊞H⭆K↧ⅉ＆$↥x-#↖x9｜²λＰe⁸” «Ｐ⊗№λκ↷¹»oＭ³→»⸿Ｍ³↓

Try it online! Link is to verbose version of code. Explanation:

Ｆ⪪↧Ｓ «

Split the lowercased input on spaces and loop over each word.

Ｆι«

Loop over each character.

Ｆ⪪”↶↖→∧gτ→|⮌!⧴KD✂‖5»⊞H⭆K↧ⅉ＆\$↥x-#↖x9｜²λＰe⁸” «

Split the compressed string fjmrwyz gnsvxz abcdefg ahiklmn bhopqrs ciotuy djkptv elquwx on spaces and loop over each letter group.

Ｐ⊗№λκ

If the group contains the current letter then draw a line in the current direction.

↷¹»

Rotate 45° clockwise.

oＭ³→»

Output the centre o and move to the position of the next letter.

⸿Ｍ³↓

Move to the start of the next word.