loading...

Daily Challenge #299 - Time to Grille!

thepracticaldev profile image dev.to staff ・1 min read

A grille cipher was a technique for encrypting plaintext by writing it onto a sheet of paper through a pierced sheet. The earliest known use is from the mathematician Girolamo Cardano in 1550. His proposal was for a rectangular stencil allowing single letters, syllables, or words to be written, then later read. The written fragments of the plaintext could be further disguised by filling the gaps between the fragments with benign words or letters.

Wikipedia: https://en.wikipedia.org/wiki/Grille_(cryptography)

Write a function that accepts two inputs: message and code. Code is a non-negative integer and should be converted to binary. Overlay the code converted to binary and the message to reveal the result.

Example

Grille("abcdef", 5)  => "df"

* convert 5 to binary:
 000101

* overlay message and code:
message : abcdef
code    : 000101
----------------
result  : df
Enter fullscreen mode Exit fullscreen mode

Tests

Grille("0abc", 2)

Grille("abcde", 32)

Grille("abcd", 1)

Good luck!


This challenge comes from dcieslak on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Discussion

pic
Editor guide
Collapse
_bkeren profile image
''

JS


const dec2bin = dec => (dec >>> 0).toString(2);

const Grille = (message, code) => {
  const codeToBinary= dec2bin(code).padStart(message.length, "0")
  return codeToBinary.split("")
    .map((binaryLetter, index) => binaryLetter === "1" ? index : '')
    .filter(String)
    .map(index => message.charAt(index)).join("")
}

Enter fullscreen mode Exit fullscreen mode
Collapse
celyes profile image
Ilyes Chouia

This is the solution in PHP, but I'm not sure if it's 100% correct since there aren't enough test cases and the test cases provided above don't have the result so they're pretty much useless.
anyway, here's my attempt:

function grille($message, $code)
{
    $letters = '';
    $code = array_filter(str_split(sprintf("%06d", decbin($code))), function($x) {
        return $x == 1;
    });
    $code = array_keys($code);
    foreach(str_split($message) as $index => $letter){
        if(in_array($index, $code)){
            $letters .= $letter;
        }
    }
    return $letters;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
peter279k profile image
peter279k

Here is the simple solution with Python:

def grille(message, code):
    bin_number = bin(code)[2:]
    bin_number_len = len(bin_number)

    if len(message) <= 1:
        return ''
    if len(message) > bin_number_len:
        bin_number = ('0' * (len(message) - bin_number_len)) + bin_number

    index = 0
    result = ''
    while index < len(message):
        if bin_number[index] == '1':
            result += message[index]
        index += 1

    if len(message) < bin_number_len:
        if len(result) == len(message):
            return result
        else:
            return ''

    return result

Enter fullscreen mode Exit fullscreen mode
Collapse
agtoever profile image
agtoever

Python 3-liner with test cases and TIO link.
Apart from some type conversions, itertools.compress does the job.

import itertools
filterlist = lambda value, length: map(int, bin(value)[2:].zfill(length))
grille = lambda string, grille: ''.join(itertools.compress(string, filterlist(grille, len(string))))

cases = [
    ('abcdef', 5),
    ('0abc', 2),
    ('abcde', 32),
    ('abcd', 1)
]
for case in cases:
    print(f'grille{case} has filter {list(filterlist(case[1], len(case[0])))} and outcome: {grille(*case)}')
Enter fullscreen mode Exit fullscreen mode

Try it online!

Collapse
rafaacioly profile image
Rafael Acioly

Scala solution

def grille(text: String, code: Int) = {
  val letters = text.split("")
  val digits = code.toBinaryString.map(_.asDigit)

  val res = letters.toList.reverse.zipAll(digits, 0, 0)

  (res collect { case (i, v) if v != 0 => i }).mkString("")
}
Enter fullscreen mode Exit fullscreen mode
Collapse
biancapower profile image
Bianca Power

ruby

def grille(message, code)
  output = ""

  # convert code to binary
  binary_code = code.to_s(2)

  # prepend the appropriate number of zeros to match length of message
  while message.length > binary_code.length
    binary_code.prepend('0')
  end

  # combine messages and binary_code into a hash
  arrays_hash = Hash[message.split("").zip(binary_code.split(""))]

  # if the value is 1, add the key to the output string
  arrays_hash.each do |k, v|
    if v == "1"
      output << k
    end
  end

  return output
end
Enter fullscreen mode Exit fullscreen mode
Collapse
citizen428 profile image
Michael Kohl

Ruby allows string interpolation in format strings, which makes generating binary_code easier:

binary_code = "%0#{message.length}d" % code.to_s(2)
Enter fullscreen mode Exit fullscreen mode

Ruby 2.7 also added a handy filter_map method, which can simplify the second half of your method:

message.chars.zip(binary_code.chars).filter_map { |c, i| c if i == "1" }
Enter fullscreen mode Exit fullscreen mode
Collapse
wheatup profile image
Hao

Another JavaScript solution, using binary operations:

const Grille = (str, bin) => [...str].filter((_, i) => 1 << (str.length-i-1) & bin).join('');
Enter fullscreen mode Exit fullscreen mode
Collapse
bugb profile image
bugb

JS solution

Grille=(m,c)=>[...m].filter((o,i)=>+[...c.toString(2).padStart(m.length,0)][i]).join``
Enter fullscreen mode Exit fullscreen mode