Skip to content

Commit

Permalink
Add keymap replacement strategy
Browse files Browse the repository at this point in the history
Adds a replacement strategy which uses knowledge of a particular keyboard
layout to create typos which would be easy mistakes on the configured
layout.

Seems to be pivoting off the wrong character in the source text right now,
but is otherwise working.
  • Loading branch information
maxfierke committed Aug 7, 2017
1 parent 9599a95 commit cbe7aea
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 0 deletions.
146 changes: 146 additions & 0 deletions data/keymaps/en-US_qwerty.yml
@@ -0,0 +1,146 @@
---
data:
'`':
shift: '~'
neighbors: ['1']
'1':
shift: '!'
neighbors: ['`', 'q']
'2':
shift: '@'
neighbors: ['1', 'q', '3']
'3':
shift: '#'
neighbors: ['2', 'w', 'e', '4']
'4':
shift: '$'
neighbors: ['3', 'e', 'r', '5']
'5':
shift: '%'
neighbors: ['4', 'r', 't', '6']
'6':
shift: '^'
neighbors: ['5', 't', 'y', '7']
'7':
shift: '&'
neighbors: ['6', 'y', 'u', '8']
'8':
shift: '*'
neighbors: ['7', 'u', 'i', '9']
'9':
shift: '('
neighbors: ['8', 'i', 'o', '0']
'0':
shift: ')'
neighbors: ['9', 'o', 'p', '-']
'-':
shift: '_'
neighbors: ['0', 'p', '[', '=']
'=':
shift: '+'
neighbors: ['-', '[', ']']
q:
shift: 'Q'
neighbors: ['1', '2', 'w', 'a']
w:
shift: 'W'
neighbors: ['q', '2', '3', 'e', 'a', 's']
e:
shift: 'E'
neighbors: ['w', '3', '4', 'r', 'd', 's']
r:
shift: 'R'
neighbors: ['e', '4', '5', 't', 'f', 'd']
t:
shift: 'T'
neighbors: ['r', '5', '6', 'y', 'g', 'f']
y:
shift: 'Y'
neighbors: ['t', '6', '7', 'u', 'h', 'g']
u:
shift: 'U'
neighbors: ['y', '7', '8', 'i', 'j', 'h']
i:
shift: 'I'
neighbors: ['u', '8', '9', 'o', 'k', 'j']
o:
shift: 'O'
neighbors: ['i', '9', '0', 'p', 'l', 'k']
p:
shift: 'P'
neighbors: ['o', '0', '-', '[', ';', 'l']
'[':
shift: '{'
neighbors: ['p', '-', '=', ']', "'", ';']
']':
shift: '}'
neighbors: ['[', '=', '\', "'"]
'\':
shift: '|'
neighbors: [']']
a:
shift: 'A'
neighbors: ['q', 'w', 's', 'z']
s:
shift: 'S'
neighbors: ['a', 'w', 'e', 'd', 'x', 'z']
d:
shift: 'D'
neighbors: ['s', 'e', 'r', 'f', 'c', 'x']
f:
shift: 'F'
neighbors: ['d', 'r', 't', 'g', 'v', 'c']
g:
shift: 'G'
neighbors: ['f', 't', 'y', 'h', 'b', 'v']
h:
shift: 'H'
neighbors: ['g', 'y', 'u', 'j', 'n', 'b']
j:
shift: 'J'
neighbors: ['h', 'u', 'i', 'k', 'm', 'n']
k:
shift: 'K'
neighbors: ['j', 'i', 'o', 'l', ',', 'm']
l:
shift: 'L'
neighbors: ['k', 'o', 'p', ';', '.', ',']
';':
shift: ':'
neighbors: ['l', 'p', '[', "'", '/', '.']
"'":
shift: '"'
neighbors: [';', '[', ']', '/']
z:
shift: 'Z'
neighbors: ['a', 's', 'x']
x:
shift: 'X'
neighbors: ['z', 's', 'd', 'c']
c:
shift: 'C'
neighbors: ['x', 'd', 'f', 'v']
v:
shift: 'V'
neighbors: ['c', 'f', 'g', 'b']
b:
shift: 'B'
neighbors: ['v', 'g', 'h', 'n']
n:
shift: 'N'
neighbors: ['b', 'h', 'j', 'm']
m:
shift: 'M'
neighbors: ['n', 'j', 'k', ',']
',':
shift: '<'
neighbors: ['m', 'k', 'l', '.']
'.':
shift: '>'
neighbors: [',', 'l', ';', '/']
'/':
shift: '?'
neighbors: ['.', ';', "'"]
' ':
shift: ' '
neighbors: ['x', 'c', 'v', 'b', 'n', 'm', ',']
1 change: 1 addition & 0 deletions src/typhar.cr
@@ -1,5 +1,6 @@
require "cli"
require "secure_random"
require "yaml"
require "./typhar/*"

module Typhar
Expand Down
3 changes: 3 additions & 0 deletions src/typhar/cli.cr
Expand Up @@ -83,6 +83,9 @@ module Typhar
when "n-shifter"
codepoint_shift = options.codepoint_shift.to_i
Typhar::ReplacementStrategies::NShifter.new(seed, codepoint_shift)
when "keymap"
keymap_name = "en-US_qwerty"
Typhar::ReplacementStrategies::Keymap.new(seed, keymap_name)
else
raise StrategyDoesNotExistException.new("'#{strategy}' does not exist.")
end
Expand Down
3 changes: 3 additions & 0 deletions src/typhar/exceptions.cr
Expand Up @@ -4,4 +4,7 @@ module Typhar

class StrategyDoesNotExistException < Exception
end

class UnknownKeyError < Exception
end
end
30 changes: 30 additions & 0 deletions src/typhar/strategies/replacement/keymap.cr
@@ -0,0 +1,30 @@
module Typhar
module ReplacementStrategies
class Keymap < Base
@keymap : Typhar::Types::Keymap

getter keymap_name
getter keymap

def initialize(@seed : UInt32, @keymap_name : String)
keymap_yml = File.read("./data/keymaps/#{keymap_name}.yml")
@keymap = Typhar::Types::Keymap.from_yaml(keymap_yml).not_nil!
@keymap.dereference!
end

def replace(to_replace : String | Char) : String | Char
keymap_entry = keymap[to_replace]?

if keymap_entry
keymap_entry.neighbors.sample(sampler)
else
raise UnknownKeyError.new("Unknown key '#{to_replace}' in keymap")
end
end

private def sampler
@sampler ||= Random.new(seed)
end
end
end
end
1 change: 1 addition & 0 deletions src/typhar/transformer.cr
@@ -1,3 +1,4 @@
require "./types/*"
require "./strategies/*"

module Typhar
Expand Down
39 changes: 39 additions & 0 deletions src/typhar/types/keymap.cr
@@ -0,0 +1,39 @@
module Typhar
module Types
class Keymap
YAML.mapping(
data: {
type: Hash(String, KeymapEntry)
}
)

def [](key)
data[key]?.not_nil!
end

def []?(key)
data.fetch(key.to_s) do |key|
result = data.find { |k, v| v.shift == key }
if result
result[1]
else
nil
end
end
end

def dereference!
data.map do |key, value|
value.neighbors = value.neighbors.flat_map do |neighbor|
if data[neighbor]?
neighbor_entry = data[neighbor]
[neighbor, neighbor_entry.shift]
else
raise UnknownKeyError.new("Unknown key '#{neighbor}' in keymap")
end
end
end
end
end
end
end
10 changes: 10 additions & 0 deletions src/typhar/types/keymap_entry.cr
@@ -0,0 +1,10 @@
module Typhar
module Types
class KeymapEntry
YAML.mapping(
shift: String,
neighbors: Array(String)
)
end
end
end

0 comments on commit cbe7aea

Please sign in to comment.