Advertisement
weemadarthur

Solitaire Cipher

May 15th, 2012
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 6.02 KB | None | 0 0
  1. (require :cl-ppcre)
  2.  
  3. ;;;; Utility functions
  4.  
  5. ;; Combine a sequence into single string, optionally putting a space between each element of the sequence
  6. (defun combine-string-parts (string-parts &optional (with-space t))
  7.   (string-right-trim " " (format nil
  8.                  (if with-space "~{~A ~}" "~{~A~}" )
  9.                  string-parts)))
  10.  
  11. ;; Divide a string into a sequence of substrings, each of length 5
  12. (defun divide-into-parts (plaintext)
  13.   "divide given plaintext into groups of five characters"
  14.   (combine-string-parts
  15.    (loop for n from 0 to (length plaintext)
  16.       when (and (> n 0) (= 0 (mod n 5)))
  17.       collect (subseq plaintext (- n 5) n))))
  18.  
  19. ;; Prepare input according to Solitaire Cipher rules - remove all punctuation and spaces,
  20. ;; convert all letters to uppercase, and split into groups of 5 characters each
  21. (defun prepare-input (input)
  22.   ;; remove spaces and punctuation, convert to uppercase
  23.   (multiple-value-bind (processed-input regex-match-p)
  24.       (cl-ppcre:regex-replace-all "[^A-Z]" (string-upcase input) "")
  25.     (declare (ignore regex-match-p))
  26.  
  27.   ;; if string length is not a multiple of 5,
  28.   ;; concatenate X's until it is
  29.   (when (not (zerop (mod (length processed-input) 5)))
  30.     (dotimes (x (- 5 (mod (length processed-input) 5)))
  31.       (setf processed-input
  32.         (concatenate 'string processed-input "X"))))
  33.  
  34.   (divide-into-parts processed-input)))
  35.  
  36. ;;;; deck handling functions
  37. (defun initialize-deck ()
  38.   (append (loop for x from 1 to 52 collect x)
  39.       '(#\A #\B)))
  40.  
  41. (defun key-deck (deck &optional (shuffle nil))
  42.   ;; should be shuffling randomly, but we keep the deck as is for unit testing
  43.   (when (eq shuffle t)
  44.     ;; shuffle the deck
  45.     deck)
  46.   deck)
  47.  
  48. ;; move given joker given number of spaces down, treating deck as a circular queue
  49. (defun move-joker-n-down (n which-joker deck)
  50.   (let (joker-index new-index)
  51.     (setf joker-index (position which-joker deck)
  52.       deck (remove-if (lambda (x) (eq x which-joker)) deck)
  53.       new-index (+ n joker-index))
  54.     (when (> new-index 53)
  55.       (setf new-index (- new-index 53)))
  56.  
  57.     (append (subseq deck 0 new-index) `(,which-joker)
  58.         (subseq deck new-index  (length deck)))))
  59.  
  60. ;; perform a triple cut - swap cards above top joker and below bottom joker
  61. (defun triple-cut (deck)
  62.   (let (top-card bottom-card joker-a-position joker-b-position)
  63.     (setf joker-a-position (position #\A deck)
  64.       joker-b-position (position #\B deck)
  65.       top-card (min joker-a-position joker-b-position)
  66.       bottom-card (max joker-a-position joker-b-position))
  67.    
  68.     (append (subseq deck (+ 1 bottom-card) (length deck))
  69.         (subseq deck top-card (+ 1 bottom-card))
  70.         (subseq deck 0 top-card))))
  71.  
  72. ;; convert a card into its corresponding value
  73. (defun get-card-value (card)
  74.   (case card
  75.       (#\A 53)
  76.       (#\B 53)
  77.       (t card)))
  78.  
  79. ;; perform a count cut - take the value of bottom card, then move that many cards
  80. ;; from top of deck to just above bottom deck
  81. (defun count-cut (deck)
  82.   (let (( bottom-card (elt deck (- (length deck) 1)))
  83.     count)
  84.     (setf count (get-card-value bottom-card))
  85.    
  86.     (append (subseq deck count 53)
  87.         (subseq deck 0 count)
  88.         `(,bottom-card))))
  89.  
  90. ;; convert a card into a character
  91. (defun card->character (card)
  92.   (case card
  93.     (#\A nil) (#\B nil)
  94.     (t (if (> card 26)
  95.        (code-char (+ 64 (- card 26)))
  96.        (code-char (+ 64 card))))))
  97.  
  98. ;; get output character
  99. (defun get-output-character (deck)
  100.   (card->character (elt deck (get-card-value (first deck)))))
  101.  
  102. ;; perform a single round of the SOlitaire cipher
  103. (defun single-round (deck)
  104.  
  105.   ;; move jokers (steps 2 and 3)
  106.   (setf deck (move-joker-n-down 2 #\B (move-joker-n-down 1 #\A deck)))
  107.  
  108.   ;; triple cut (step 4)
  109.   (setf deck (triple-cut deck))
  110.  
  111.   ;; count cut (step 5)
  112.   (setf deck (count-cut deck))
  113.  
  114.   ;; return deck
  115.   deck)
  116.  
  117. (defun not-nil (x)
  118.   (not (eq nil x)))
  119.  
  120. ;; get keystream - not sure how to do a loop until condition is met, so
  121. ;; generating twice as many characters as needed, then taking first few
  122. (defun get-keystream (deck key-length)
  123.  
  124.   ;; generate large number of characters
  125.   ;; then extract required number of characters
  126.   (combine-string-parts
  127.    (subseq (remove-if-not 'not-nil
  128.               (loop for n from 1 to (* 2 key-length)
  129.                  do (setf deck (single-round deck))
  130.                  collect (get-output-character deck)))
  131.        0 key-length) nil))
  132.  
  133. ;; encrypt given text using Solitaire cipher
  134. (defun encrypt (plaintext)
  135.   (let (keystream (ciphertext '()))
  136.     (setf keystream (prepare-input (get-keystream (initialize-deck) (length plaintext)))
  137.       plaintext (prepare-input plaintext))
  138.     (dotimes (n (length plaintext) (prepare-input (reverse (combine-string-parts ciphertext nil))))
  139.       (if (not (char-equal (char plaintext n) #\Space))
  140.       (let* ((plaintext-char-value (- (char-code (char plaintext n)) 64))
  141.          (keystream-char-value (- (char-code (char keystream n)) 64)))
  142.         (setf plaintext-char-value (+ plaintext-char-value keystream-char-value))
  143.         (when (> plaintext-char-value 26)
  144.           (setf plaintext-char-value (- plaintext-char-value 26)))
  145.         (push (code-char (+ 64 plaintext-char-value)) ciphertext))
  146.       (push #\Space ciphertext)))))
  147.  
  148. ;; decrypt given text using Solitaire cipher
  149. (defun decrypt (ciphertext)
  150.   (let (keystream (plaintext '()))
  151.     (setf keystream (prepare-input (get-keystream (initialize-deck) (length ciphertext)))
  152.       ciphertext (prepare-input ciphertext))    ;; may not be necessary
  153.     (dotimes (n (length ciphertext) (prepare-input (reverse (combine-string-parts plaintext nil))))
  154.       (if (not (char-equal (char ciphertext n) #\Space))
  155.       (let* ((ciphertext-char-value (- (char-code (char ciphertext n)) 64))
  156.          (keystream-char-value (- (char-code (char keystream n)) 64)))
  157.         (when (<= ciphertext-char-value keystream-char-value)
  158.           (setf ciphertext-char-value (+ 26 ciphertext-char-value)))       
  159.         (setf ciphertext-char-value (- ciphertext-char-value keystream-char-value))
  160.         (push (code-char (+ 64 ciphertext-char-value)) plaintext))
  161.       (push #\Space plaintext)))))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement