| Bytes | Lang | Time | Link |
|---|---|---|---|
| nan | Scala 3 | 240510T114217Z | 138 Aspe |
| 557 | Rust | 240510T131221Z | mousetai |
| 569 | Python 3 | 140307T132931Z | Michael |
| nan | Python | 240510T120204Z | 138 Aspe |
| 111 | Pyth | 170924T130412Z | EndingBe |
| 330 | Ruby | 140307T014848Z | Doorknob |
Scala 3, 1492 766 747 740 bytes
A port of @Doorknob's Ruby answer in Scala.
Saved 726+19+7=752 bytes thanks to @ceilingcat
Golfed version. Attempt This Online!
object m{def t()=scala.io.StdIn.readLine().trim.replace('j','i').toUpperCase.filter(_.isLetter).toList
def f(t:Array[Array[Char]],c:Char):(Int,Int)={t.zipWithIndex.collectFirst{case(r,i)if r.contains(c)=>(i,r.indexOf(c))}.getOrElse((0,0))}
def main(A:Array[String])={val k=t().distinct
var F=(k++("ABCDEFGHIKLMNOPQRSTUVWXYZ".filterNot(k.contains))).toArray
val T=Array.tabulate(5,5)((r,c)=>F(r*5+c))
val s=(List[Char]()/:t()){(a,c)=>if(a.nonEmpty&&a.last==c)a:+'X':+c else a:+c}
println(s"${(if(s.size%2<1)s else s:+'X').sliding(2,2).toList.flatMap{p=>val(g,h)=(p(0),p.last)
val(r,c)=f(T,g)
val(q,d)=f(T,h)
if(r==q){List(T(r)((c+1)%5),T(q)((d+1)%5))}else if(c==d){List(T((r+1)%5)(c),T((q+1)%5)(d))}else{List(T(r)(d),T(q)(c))}}.mkString}")}}
Ungolfed version. Attempt This Online!
object Main {
// Function to transform the input string
def transform(text: String): List[Char] = {
// Replace 'j' with 'i', convert to uppercase, remove non-alphabet characters, and split into characters
text.replace('j', 'i').toUpperCase.filter(_.isLetter).toList
}
// Function to generate the Playfair cipher key table
def generateKeyTable(key: String): Array[Array[Char]] = {
val keyChars = transform(key).distinct
val remainingChars = "ABCDEFGHIKLMNOPQRSTUVWXYZ".filterNot(keyChars.contains)
val combinedChars = (keyChars ++ remainingChars).toArray
// Create the 5x5 key table
Array.tabulate(5, 5)((row, col) => combinedChars(row * 5 + col))
}
// Function to prepare the message for encryption
def prepareMessage(msg: String): List[List[Char]] = {
val msgChars = transform(msg)
val preparedMsg = msgChars.foldLeft(List[Char]()) { (acc, char) =>
if (acc.nonEmpty && acc.last == char) acc :+ 'X' :+ char else acc :+ char
}
val evenPreparedMsg = if (preparedMsg.size % 2 == 0) preparedMsg else preparedMsg :+ 'X'
evenPreparedMsg.sliding(2, 2).toList
}
// Function to find coordinates of a character in the key table
def findCoords(tbl: Array[Array[Char]], char: Char): (Int, Int) = {
tbl.zipWithIndex.collectFirst {
case (row, rowIndex) if row.contains(char) => (rowIndex, row.indexOf(char))
}.getOrElse(throw new NoSuchElementException(s"Character $char not found in the table"))
}
// Function to encrypt a message using the Playfair cipher
def encryptMessage(tbl: Array[Array[Char]], msg: List[List[Char]]): String = {
msg.flatMap { pair =>
val (c1, c2) = (pair.head, pair.last)
val (c1Row, c1Col) = findCoords(tbl, c1)
val (c2Row, c2Col) = findCoords(tbl, c2)
if (c1Row == c2Row) {
// Same row
List(tbl(c1Row)((c1Col + 1) % 5), tbl(c2Row)((c2Col + 1) % 5))
} else if (c1Col == c2Col) {
// Same column
List(tbl((c1Row + 1) % 5)(c1Col), tbl((c2Row + 1) % 5)(c2Col))
} else {
// Rectangle swap
List(tbl(c1Row)(c2Col), tbl(c2Row)(c1Col))
}
}.mkString
}
def main(args: Array[String]): Unit = {
val key = scala.io.StdIn.readLine().trim
val msg = scala.io.StdIn.readLine().trim
// Generate the key table and prepare the message
val keyTable = generateKeyTable(key)
val preparedMsg = prepareMessage(msg)
// Encrypt the message
val encryptedMsg = encryptMessage(keyTable, preparedMsg)
// Output the encrypted message
println(s"Encrypted Message: $encryptedMsg")
}
}
Rust, 583 578 bytes 557 bytes
use std::io::*;fn f(mut k:Vec<u8>,t:Vec<u8>){let u=|a:Vec<u8>|a.into_iter().flat_map(|mut a|{a|=32;(a>96&&a<123).then(||a-(a==106)as u8)});k.extend(97..123);let mut o=vec![];for a in u(k){if!o.contains(&a){o.push(a)}}let mut p=vec![];for c in u(t){if p.len()%2>0&&p[p.len()-1]==c{p.push(120)}p.push(c)}if p.len()%2>0{p.push(120)}for k in 0..p.len()/2{let[d,e]=[0,1].map(|z|o.iter().position(|&a|a==p[k*2+z]).unwrap());stdout().write(&if d%5==e%5{[(d+5)%25,(e+5)%25]}else if d/5==e/5{[d/5*5+(d+1)%5,e/5*5+(e+1)%5]}else{[d/5*5+e%5,e/5*5+d%5]}.map(|z|o[z]));}}
Formatted
use std::io::*;
fn f(mut k: Vec<u8>, t: Vec<u8>) {
let u = |a: Vec<u8>| {
a.into_iter().flat_map(|mut a| {
a |= 32;
(a > 96 && a < 123).then(|| a - (a == 106) as u8)
})
};
k.extend(97..123);
let mut o = vec![];
for a in u(k) {
if !o.contains(&a) {
o.push(a)
}
}
let mut p = vec![];
for c in u(t) {
if p.len() % 2 > 0 && p[p.len() - 1] == c {
p.push(120)
}
p.push(c)
}
if p.len() % 2 > 0 {
p.push(120)
}
for k in 0..p.len() / 2 {
let [d, e] = [0, 1].map(|z| o.iter().position(|&a| a == p[k * 2 + z]).unwrap());
stdout().write(
&if d % 5 == e % 5 {
[(d + 5) % 25, (e + 5) % 25]
} else if d / 5 == e / 5 {
[d / 5 * 5 + (d + 1) % 5, e / 5 * 5 + (e + 1) % 5]
} else {
[d / 5 * 5 + e % 5, e / 5 * 5 + d % 5]
}
.map(|z| o[z]),
);
}
}
Python 3, 709 705 685 664 662 615 569
With @ceilingcat's improvements. Accepts input from stdin.
import itertools as I,re,string
a=string.ascii_uppercase
d=lambda x:I.product(range(5),repeat=x)
t=lambda x:(input()+x).upper().replace('J','I')
s=''
for _ in t(a):
if _ not in s and _ in a:s+=_
m=[s[i:i+5]for i in range(0,len(s),5)]
e={r[i]+r[j]:r[-~i%5]+r[-~j%5]for r in m for i,j in d(2)if i-j}
e.update({c[i]+c[j]:c[-~i%5]+c[-~j%5]for c in zip(*m)for i,j in d(2)if i-j})
e.update({m[q][r]+m[u][v]:m[q][v]+m[u][r]for q,r,u,v in d(4)if(q-u)*(r-v)})
print(''.join(e[a+(b if b else'X')]for a,b in re.findall(r'(.)(?:(?!\1)(.))?',''.join([_ for _ in t('')if _ in a]))))
Example:
mfukar@oxygen[/tmp]<>$ python playfair.py
Stack Overflow
The cat crept into the crypt, crapped, and crept out again.
SIRACARDFMVUICVSMORDZNAKECMZMFBCYNRDFMSVTVKBTMMY
Python, 1338 525 bytes
A port of @Doorknob's Ruby answer in Python.
Saved 813 bytes thanks to @ceilingcat
Golfed version. Try It Online!
L=len
t=lambda:[c for c in input().strip().replace('j','i').upper()if c.isalpha()]
u=list(dict.fromkeys(t()))
m=t()
j=0
p=e=''
while j<L(m):
p+=m[j];j+=1
if j<L(m)and m[j-1]==m[j]:p+='X'
if L(p)%2:p+='X'
l=[(u+[h for h in"ABCDEFGHIKLMNOPQRSTUVWXYZ"if h not in u])[j:j+5]for j in range(0,25,5)]
def f(h):
for i,o in enumerate(l):
if h in o:return i,o.index(h)
for c,C in[p[j:j+2]for j in range(0,L(p),2)]:r,i=f(c);R,I=f(C);e+=l[r][-~i%5]+l[R][-~I%5]if r==R else l[r][I]+l[R][i]if i-I else l[-~r%5][i]+l[-~R%5][I]
print(e)
Ungolfed version. Attempt This Online!
# Function to transform the input string
def transform(text):
# Replace 'j' with 'i', convert to uppercase, remove non-alphabet characters, and split into characters
text = text.replace('j', 'i').upper()
return [char for char in text if char.isalpha()]
# Function to generate the Playfair cipher key table
def generate_key_table(key):
key = transform(key)
# Create a list of unique characters in the key followed by the rest of the alphabet (excluding 'J')
unique_key_chars = list(dict.fromkeys(key))
remaining_chars = [char for char in "ABCDEFGHIKLMNOPQRSTUVWXYZ" if char not in unique_key_chars]
combined_chars = unique_key_chars + remaining_chars
# Create the 5x5 key table
return [combined_chars[i:i+5] for i in range(0, 25, 5)]
# Function to prepare the message for encryption
def prepare_message(msg):
msg = transform(msg)
# Add 'X' between repeating characters
prepared_msg = []
i = 0
while i < len(msg):
prepared_msg.append(msg[i])
if i + 1 < len(msg) and msg[i] == msg[i + 1]:
prepared_msg.append('X')
i += 1
# Ensure even length by adding 'X' if necessary
if len(prepared_msg) % 2 != 0:
prepared_msg.append('X')
# Split into pairs
return [prepared_msg[i:i+2] for i in range(0, len(prepared_msg), 2)]
# Function to find coordinates of a character in the key table
def find_coords(tbl, char):
for row_idx, row in enumerate(tbl):
if char in row:
return row_idx, row.index(char)
return None
# Function to encrypt a message using the Playfair cipher
def encrypt_message(tbl, msg):
encrypted_msg = []
for pair in msg:
c1, c2 = pair
c1_row, c1_col = find_coords(tbl, c1)
c2_row, c2_col = find_coords(tbl, c2)
if c1_row == c2_row:
# Same row
encrypted_msg.append(tbl[c1_row][(c1_col + 1) % 5])
encrypted_msg.append(tbl[c2_row][(c2_col + 1) % 5])
elif c1_col == c2_col:
# Same column
encrypted_msg.append(tbl[(c1_row + 1) % 5][c1_col])
encrypted_msg.append(tbl[(c2_row + 1) % 5][c2_col])
else:
# Rectangle swap
encrypted_msg.append(tbl[c1_row][c2_col])
encrypted_msg.append(tbl[c2_row][c1_col])
return ''.join(encrypted_msg)
# Get user input for the key and message
key = input().strip()
msg = input().strip()
# Generate the key table and prepare the message
key_table = generate_key_table(key)
prepared_msg = prepare_message(msg)
# Encrypt the message
encrypted_msg = encrypt_message(key_table, prepared_msg)
# Output the encrypted message
print(encrypted_msg)
Pyth - 111
Too late for competing, I just wanted to share. Here's the encoder and decoder
L@G:rb0\j\iJ{y+wGKywWhZ=Zh*2xlR{RcK2 1IhZ=KXZK\x;M@J+G?!eH5?!hH?q4%G5_4 1eHVcK2A,xJhNxJeN=Z-V.DH5.DG5pgGZpgH_RZ
Explanation:
L b L defines common method y(b); 2 calls helps us saving two bytes
r 0 lowercase r(b,0)
: \j\i : replaces all occurrences of "j" with "i"
@G strips all non-alphabetic characters; G = pyth built-in alphabet
w first input argument
+ G appends the alphabet (G)
y calls y(b)
{ { makes set (removes duplicated characters)
J assigns result to 'J' (KEY VARIABLE)
Kyw assigns output from y(second input argument) to 'K' (TEXT VARIABLE)
WhZ ; While (Z+1 != 0) <-> While (Z != -1) <-> While mismatched items found
cK2 list of K pairs. e.g. 'ABCCDDE' -> [AB, CC, DD, E]
lR{R l length of { unique characters. e.g. [2, 1, 1, 1]
x 1 1-length first index. e.g. 1
h*2 *2+1 (Index in K) e.g. 3 'ABC CDDE'
=Z Assigns to 'Z'
IhZ if (Z != -1) <-> if (mismatched found)
=KXZK\x X Inserts at Z index in K an 'x' and reassigns to 'K' e.g. 'ABCXC...'
M M defines function g(G, H) where G index H vector (INDEX CONVERSION)
?!eH if (same col)
5 then +5
?!hH else { if (same row)
?q4%G5 then if (last col)
_4 then -4
1 else +1
eH else col
+G index += increment
@J J[index]
VcK2 V loops over cK2 list of K pairs
,xJhNxJeN x returns pair members index in J
A A assigns G = xJhN, H = xJeN
.DH5 .D returns [row, col] = [i/5,i%5] of 5xn matrix from index of H
.DG5 idem. of G
-V Subtracts vectors (RELATIVE POSITION)
=Z Assigns to 'Z'
pgGZ p prints g(G, Z) return value
pgH_RZ p prints g(H, _RZ) return value, and _R changes signs of Z vector
Sample Key/Message/Output:
Stack Overflow
Gottfried Leibniz is famous for his slogan Calculemus, which means Let us calculate. He envisioned a formal language to reduce reasoning to calculation.
lfaukvvnrbbomwpmupkoexvqkovfimaqohflcmkcdsqwbxqtlintinbehcbovttksbtybsavmormwuthrhrbkevfxebqbspdxtbfsvfrwyarfrctrhmpwkrssbtybsvurh
Ruby, 461 411 366 359 352 346 330 characters
k,m=$<.map{|x|x.tr(?j,?i).upcase.tr('^A-Z','').chars}
t=[*((k&k)|[*?A..?Z]-[?J]).each_slice(5)]
m=(m*'').gsub(/(.)\1/,'\1X\1').chars
c=->n{[t.index{|r|k=r.index n},k]}
$><<(m.size%2<1?m:m+[?X]).each_slice(2).map{|p,q|a,b,d,e=*c[p],*c[q]
a==d ?[t[a][(b+1)%5],t[d][(e+1)%5]]:b==e ?[t[(a+1)%5][b],t[(d+1)%5][e]]:[t[a][e],t[d][b]]}*''
Thanks to @daniero for saving... err, a lot of bytes. \o/
Here's the ungolfed code:
key = gets.chomp
msg = gets.chomp
transform = ->str{
str.gsub! 'j', 'i'
str.upcase!
str.gsub! /[^A-Z]/, ''
str.split('')
}
# 1. Generate a key table
key = transform[key]
chars = key.uniq + ([*?A..?Z] - key - ['J'])
tbl = Array.new(5) {
Array.new(5) {
chars.shift
}
}
# 2. Prepare the message
msg = transform[msg]
msg = msg.join('').gsub(/(.)\1/){ "#{$1}X#{$1}" }.split('')
msg = (msg.length % 2 == 0 ? msg : msg + ['X']).each_slice(2).to_a
# 3. Encryption
coords = ->chr{
i = -1
[tbl.index{|row| i = row.index chr}, i]
}
msg.map! do |c1, c2|
c1, c2 = coords[c1], coords[c2]
if c1[0] == c2[0]
# same row
[tbl[c1[0]][(c1[1] + 1) % 5], tbl[c2[0]][(c2[1] + 1) % 5]]
elsif c1[1] == c2[1]
# same column
[tbl[(c1[0] + 1) % 5][c1[1]], tbl[(c2[0] + 1) % 5][c2[1]]]
else
# neither
[tbl[c1[0]][c2[1]], tbl[c2[0]][c1[1]]]
end
end
# Output!
puts msg.join
Here's some sample outputs:
llama@llama:...code/ruby/ppcg23276playfair$ printf 'Stack Overflow\nThe cat crept into the crypt, crapped, and crept out again.\n' | ./playfair.rb; printf 'This is a password!\nProgramming Puzzles and Code Golf is a Stack Exchange site.\n' | ./playfair.rb
SIRAVXRDFMVUUYVSBLRDZNYVECMZMFBCYNRDFMSVTVKBVBMY
WDDEDSXIXOQFBTUYVQFISQWGRPFBWMESATAHHGMBVEITQFFISHMI