10
\$\begingroup\$

Pigpen Cipher Encryption

Your mission is simple: to write a program which receives text as input and outputs an ASCII representation of it in the Pigpen Cipher.

Input

ASCII-only (no Unicode) characters. You must be able to handle at least 16384 characters and 256 lines.

Output

  • Replace all characters which are not new lines or in ABCDEFGHIJKLMNOPQRSTUVWXYZ or abcdefghijklmnopqrstuvwxyz with spaces.
  • Case-insensitively, replace each letter with its encrypted form (see next section), inserting a trailing space on each of the three lines after each. Each encoded character is a 3*3 block of ASCII art. Replace spaces with 3*3 blocks of spaces.
  • All the characters should be on the same three lines (call these three lines one pigpen-line), unless there is a new line, which starts a new pigpen-line. Leave a normal line blank between pigpen-lines.

The Cipher

  |       | |       |
 .|       |.|       |.
--+ for J +-+ for K +-- for L

--+       +-+       +--
 .|       |.|       |.
--+ for M +-+ for N +-- for O

--+       +-+       +--
 .|       |.|       |.
  | for P | | for Q |   for R

(ABCDEFGHI are the same as JKLMNOPQR, but with the . replaced by a space)

\./
 V  for W (note the row of 3 spaces on the top)

 ^
/.\ for Z (note the 3 spaces on the bottom)


 \
 .>
 /  for X (note the leading spaces)

 /
<.
 \  for Y (note extra trailing spaces)

(STUV are like WXYZ, but with the . replaced by a space)

Example

The input "hEllo,wORLd" should produce:

+-+ +-+ |   |   +--         +-- +-- |   --+
| | | | |.  |.  |.      \./ |.  |.  |.    |
| | +-+ +-- +-- +--      V  +-- |   +-- --+

The base64 encoding of the above, with a trailing new line, is below. The md5sum is 6f8ff1fed0cca4dd0492f9728ea02e7b.

Ky0rICstKyB8ICAgfCAgICstLSAgICAgICAgICstLSArLS0gfCAgIC0tKwp8IHwgfCB8IHwuICB8
LiAgfC4gICAgICBcLi8gfC4gIHwuICB8LiAgICB8CnwgfCArLSsgKy0tICstLSArLS0gICAgICBW
ICArLS0gfCAgICstLSAtLSsK

Without a trailing new line, the md5sum is 581005bef7ee76e24c019d076d5b375f and the base64 is:

Ky0rICstKyB8ICAgfCAgICstLSAgICAgICAgICstLSArLS0gfCAgIC0tKwp8IHwgfCB8IHwuICB8

LiAgfC4gICAgICBcLi8gfC4gIHwuICB8LiAgICB8CnwgfCArLSsgKy0tICstLSArLS0gICAgICBW ICArLS0gfCAgICstLSAtLSs=

Rules

  • Standard loopholes are forbidden.
  • This is code golf. Shortest code wins.
  • In languages which cannot accept multiple lines of input at a time (e.g. JavaScript with prompt()), use * (or some other character) as a line separator.

Erranda

  • The example was missing a few spaces (A space should consist of the trailing spaces of the previous character, if any, itself, and its own trailing spaces). This has now been fixed.
\$\endgroup\$
  • 1
    \$\begingroup\$ I remember playing with this as a kid, but never knew the name of it. Thanks! \$\endgroup\$ – Geobits Sep 11 '14 at 19:30
  • \$\begingroup\$ Can I use a custom font? :-) \$\endgroup\$ – Florian F Sep 11 '14 at 19:47
  • \$\begingroup\$ @FlorianF No... \$\endgroup\$ – user16402 Sep 11 '14 at 19:52
  • \$\begingroup\$ Coding in javascript. Usually prompt is used for input, but it accepts a single line only. Could I use a special char (like '*') to mark a new line in input?. \$\endgroup\$ – edc65 Sep 13 '14 at 18:04
  • \$\begingroup\$ @edc65 Yes, you may \$\endgroup\$ – user16402 Sep 13 '14 at 18:11
4
\$\begingroup\$

JavaScript (ES6) 312 327 340 372 446

Not counting indentation white space and newlines - could be golfed more. Using an '*' to mark new lines in input, as prompt accepts a single line.

console.log(prompt().split('*').map(s=>
  [0,1,2].map(r=>
  [...s].map(c=>o+=n+
    '   \\1/ \\ /1\\ /  V  ^  1><1 --++-++-- 1||1||1 '.substr(
    (c=(32|c.charCodeAt())-97)<0|c>25?0:c<18
     ?27+3*'330441552030141252033144255'[f=8,c%9*3+r]
     :3*'482630015274'[f=21,c%4*3+r],3,n=' ')
  .replace(1,' .'[r&c>f])
  ,n='\n'),o=''
)&&o).join('\n'))

Test in FireFox/FireBug console

Input: ABCDEFGHI*JKLMNOPQR*STUV*WXYZ*HeLlO WoRlD!

  | | | |   --+ +-+ +-- --+ +-+ +--
  | | | |     | | | |     | | | |  
--+ +-+ +-- --+ +-+ +--   | | | |  

  | | | |   --+ +-+ +-- --+ +-+ +--
 .| |.| |.   .| |.| |.   .| |.| |. 
--+ +-+ +-- --+ +-+ +--   | | | |  

     \   /   ^ 
\ /   > <   / \
 V   /   \     

     \   /   ^ 
\./  .> <.  /.\
 V   /   \     

+-+ +-+ |   |   +--         +-- +-- |   --+    
| | | | |.  |.  |.      \./ |.  |.  |.    |    
| | +-+ +-- +-- +--      V  +-- |   +-- --+      
\$\endgroup\$
  • \$\begingroup\$ You have 5 spaces between the o and w in "Hello world", the OP only has 4 \$\endgroup\$ – Claudiu Sep 14 '14 at 23:15
  • \$\begingroup\$ @Claudiu each character (even blank, char 32) 3 spaces, separator between chars 1 space. 1+3+1 == 5. I'll check with OP \$\endgroup\$ – edc65 Sep 15 '14 at 7:45
  • \$\begingroup\$ Yea it definitely makes more sense with 5. I thought he specifically wanted 4 as a special-case, but good thing you asked! \$\endgroup\$ – Claudiu Sep 15 '14 at 15:36
3
\$\begingroup\$

C# - 921 720

Obviously not a winning entry, but this looked like too much fun to pass off :)

Program takes input as a single, then prints the pigpen. To input multiple lines, use an underscore (_) as seen in output.

Code

using System;class P{static void Main(){
int i,y,j,k,w,z;string[]g=Console.ReadLine().ToLower().Split('_');
var d="_________  |b .|b--+_| |b|.|b+-+_|  b|. b+--_--+b .|b--+_+-+b|.|b+-+_+--b|. b+--_--+b .|b  |_+-+b|.|b| |_+--b|. b|  _____   b\\./b v _ \\ b .>b / _ / b<. b \\ _ ^ b/.\\b   _  b  b  ".Replace('b','\n').Split('_');
for(i=0;i<d.Length;i++){if(d[i]==""){d[i]=i<17?d[i+9]:d[i+4];d[i]=d[i].Replace('.',' ');}}
for(y=0;y<g.Length;y++){string o="",s,e=g[y];var r=new string[z=e.Length][];
for(i=0;i<z;i++){if(e[i]-97<0|e[i]-97>25)e=e.Replace(e[i],'{');
o+=d[e[i]-97]+'_';r[i]=(o.Split('_')[i].Split('\n'));}
for(j=0;j<3;j++)for(k=0;k<(w=r.Length);k++){
s=r[k][j];Console.Write(k==w-1?s+'\n':s+' ');}}
Console.ReadLine();}}

Concept

The cipher uses a some character sequences that get duplicated quite a bit. For example, '--' shows up 16 times and '__' (two spaces) shows up 20 times. I replace these sequences with single-character symbols and switch them out at runtime, cutting the number of characters needed to store the pigpen cipher in half. Similarly, a newline usually requires two characters, but is replaced by a symbol (n) and switched out later.

The program handles multiple lines of input by splitting the input into an array where each element is a single line of input. The program then simply runs the cipher on each line separately.

This is my first golf in any language, so there is probably a lot that can be done to improve this code.

Output

hEllo,wORLd
+-+ +-+ |   |   +--        +-- +-- |   --+
| | | | |.  |.  |.     \./ |.  |.  |.    |
| | +-+ +-- +-- +--     v  +-- |   +-- --+

code_golf
|   +-- --+ +-+
|   |.    | | |
+-- +-- --+ +-+
--+ +-- |   +--
  | |.  |.  |
  | +-- +-- +--

multi_line_input
--+  /  |    \  +--
 .| <   |.    > |
--+  \  +--  /  |
|   +-- +-+ +-+
|.  |   |.| | |
+-- |   +-+ +-+
+-- +-+ --+  /   \
|   |.|  .| <     >
|   +-+   |  \   /
\$\endgroup\$
  • 1
    \$\begingroup\$ I don't know C#, but there appears to be an unnecessary space in for(int i=0;i<p.Length; i++) (near the middle) \$\endgroup\$ – user16402 Sep 13 '14 at 11:42
  • \$\begingroup\$ Thanks professorfish, there was another one I missed as well \$\endgroup\$ – jrbuchner Sep 13 '14 at 12:02
  • 1
    \$\begingroup\$ In m♥lti \n lin♣e \n input, my phone renders that as having a heart instead of u in multi, and a club between the n and the e in line. Why is that? \$\endgroup\$ – Beta Decay Sep 14 '14 at 7:00
  • 2
    \$\begingroup\$ @BetaDecay I think it is intended, to show the handling of low ASCII chars \$\endgroup\$ – edc65 Sep 14 '14 at 7:24
  • \$\begingroup\$ The heart and club were put there to demonstrate compliance with the challenges' rule : Replace all characters which are not new lines or in ABCDEFGHIJKLMNOPQRSTUVWXYZ or abcdefghijklmnopqrstuvwxyz with spaces \$\endgroup\$ – jrbuchner Sep 14 '14 at 7:45
2
\$\begingroup\$

Python 2, 180 + 78 + 1 + 3 = 262 characters

The 180-byte program (last two newlines are tabs):

L=open('f','rb').read().decode('zip')
while 1:
 n=raw_input()
 for s in(0,3,6):
    for c in n:w=ord(c.lower())-97;print''.join(L[w+(s+i)*26]for i in(0,1,2))if-1<w<27 else'  ',
    print

Requires a 78-byte file called 'f' to be same directory (+1 byte for filename), which contains the following:

$ hexdump f
0000000 9c78 a853 d1a9 d6d5 2206 3805 0103 c174
0000010 c100 8b88 07d1 9ae1 051a 4ab0 385d ae03
0000020 2803 8a82 3a80 406c ae18 0f42 6006 0c1c
0000030 0a2d 31fa 6076 ee8c a030 0e14 2987 8428
0000040 7501 3080 c39a 5a10 0014 21c7 7333
000004e

The base64 encoding of file f is:

eJxTqKnR1dYGIgU4AwF0wQDBiIvRB+GaGgWwSl04A64DKIKKgDpsQBiuQg8GYBwMLQr6MXZgjO4w
oBQOhykohAF1gDCawxBaFADHITNz

The program exits with an exception, 2>_ suppresses the error (+3 bytes):

$ echo "hEllo,wORLd" | python pigpen.py 2>_| ./md5.py
7ed49b7013a30cc3e84aa807f6585325

Explanation:

I created a look-up table, L, which is a mapping of {position_in_3x3_block: {letter_being_encrypted: symbol_for_letter_at_position}}, stored in a flat array. The program prints the letters by doing a simple look-up for each position.

\$\endgroup\$
  • \$\begingroup\$ You should add one byte for f and three bytes for 2>_ \$\endgroup\$ – user16402 Sep 15 '14 at 7:23
0
\$\begingroup\$

Perl 5 -lF, 297 288 261 231 bytes

$,=$";$_=' .||.||. ';chomp@F;map{@r=/.../g;say map{/[a-z]/i?$r[-65+ord uc]:$"x3}@F}(y/./ /r.y/.| /-+-/r x2)x2 .'    \\  /  ^ 'x2,y/./ /r x3 .$_ x3 .'\\ /  ><  / \\\\./ .><. /.\\',(y/.| /-+-/r x2 .y/./ /r)x2 .' V  /  \\    'x2;say''

Try it online!

There's probably still a bit here that could be golfed further.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy