Last active
February 15, 2025 00:25
-
-
Save kenorb/2b03fd495a9f43a0dc7bac9dd1542d87 to your computer and use it in GitHub Desktop.
Find surrounding keys on keyboard layouts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
keyboard_layouts = { | |
# US QWERTY Layout (Lowercase) | |
"us_lowercase": [ | |
"`1234567890-=", | |
" qwertyuiop[]\\", | |
" asdfghjkl;'", | |
" zxcvbnm,./" | |
], | |
# US QWERTY Layout (Uppercase) | |
"usuppercase": [ | |
"~!@#$%^&*()+", | |
" QWERTYUIOP{}|", | |
" ASDFGHJKL:\"", | |
" ZXCVBNM<>?" | |
], | |
# UK QWERTY Layout (Lowercase) | |
"uk_lowercase": [ | |
"§±1234567890-=", | |
" qwertyuiop[]#", | |
" asdfghjkl;'", | |
" zxcvbnm,./" | |
], | |
# UK QWERTY Layout (Uppercase) | |
"ukuppercase": [ | |
"±§!@#$%^&*()+", | |
" QWERTYUIOP{}~", | |
" ASDFGHJKL:\"", | |
" ZXCVBNM<>?" | |
] | |
} | |
def find_surroundings(keyboard_layout, layout_name): | |
# Convert layout into a 2D matrix for easier processing | |
matrix = keyboard_layout[layout_name] | |
# Find max row length to pad shorter rows | |
max_length = max(len(row) for row in matrix) | |
padded_matrix = [row.ljust(max_length) for row in matrix] | |
results = [] | |
# Iterate through each row and character | |
for row_idx, row in enumerate(padded_matrix): | |
for col_idx, char in enumerate(row): | |
if char == ' ': # Skip space characters | |
continue | |
surroundings = [] | |
# Check all 8 directions around the character | |
for dr in [-1, 0, 1]: | |
for dc in [-1, 0, 1]: | |
if dr == 0 and dc == 0: # Skip the character itself | |
continue | |
new_row = row_idx + dr | |
new_col = col_idx + dc | |
# Check if the position is valid | |
if (0 <= new_row < len(padded_matrix) and | |
0 <= new_col < len(padded_matrix[new_row])): | |
neighbor = padded_matrix[new_row][new_col] | |
if neighbor != ' ': # Only add non-space characters | |
surroundings.append(neighbor) | |
results.append(f"{char}[{''.join(surroundings)}]") | |
return results | |
# Process each layout | |
for layout_name in keyboard_layouts: | |
print(f"\n=== {layout_name} ===") | |
surroundings = find_surroundings(keyboard_layouts, layout_name) | |
for result in surroundings: | |
print(result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
def generate_adjacent_sequences_from_grid(grid, min_len=2, max_len=4): | |
""" | |
Walk the grid in four directions (horizontal right, vertical down, | |
diagonal down-right, and diagonal down-left) and collect all contiguous | |
strings of length between min_len and max_len. Also add each sequence's reverse. | |
""" | |
sequences = set() | |
rows = len(grid) | |
cols = max(len(row) for row in grid) | |
# For each cell in the grid: | |
for r in range(rows): | |
for c in range(cols): | |
# If this row doesn't have a cell at index c or it's None, skip. | |
if c >= len(grid[r]) or grid[r][c] is None: | |
continue | |
start_char = grid[r][c] | |
# For each of the four directions: | |
for dr, dc in [(0, 1), (1, 0), (1, 1), (1, -1)]: | |
seq = start_char | |
nr, nc = r, c | |
# Build sequences by stepping up to max_len-1 additional keys. | |
for _ in range(1, max_len): | |
nr += dr | |
nc += dc | |
# Check bounds: | |
if nr < 0 or nr >= rows or nc < 0 or nc >= cols: | |
break | |
# If this row does not have a cell at index nc or it is None, stop. | |
if nc >= len(grid[nr]) or grid[nr][nc] is None: | |
break | |
seq += grid[nr][nc] | |
if len(seq) >= min_len: | |
sequences.add(seq) | |
sequences.add(seq[::-1]) | |
return sequences | |
# --- Define grids for several layouts --- | |
# The grids below are approximate and assume that missing keys are represented by None. | |
# They use fixed-width rows so that physical offsets are approximated by None entries. | |
# QWERTY (letters only) | |
# Row0: 10 keys; Row1: offset by one (None in col0); Row2: offset by two (None in col0-1) | |
grid_qwerty = [ | |
list("qwertyuiop"), | |
[None] + list("asdfghjkl"), | |
[None, None] + list("zxcvbnm") + [None] # padded to 10 columns | |
] | |
# QWERTZ – similar to QWERTY but with "qwertzuiop" in row0 and a slight difference on row2. | |
grid_qwertz = [ | |
list("qwertzuiop"), | |
[None] + list("asdfghjkl"), | |
[None, None] + list("yxcvbnm") + [None] | |
] | |
# AZERTY – note that the home and bottom rows differ in letters. | |
grid_azerty = [ | |
list("azertyuiop"), | |
[None] + list("qsdfghjklm"), # assume home row is offset by one | |
[None, None] + list("wxcvbn") + [None, None] # pad to 10 columns (2+6+2) | |
] | |
# Dvorak – using a common letter layout (all rows assumed aligned for simplicity) | |
grid_dvorak = [ | |
list("',.pyfgcrl"), | |
list("aoeuidhtns"), | |
list(";qjkxbmwvz") | |
] | |
# Colemak – a common Colemak layout (again, assuming aligned rows) | |
grid_colemak = [ | |
list("qwfpgjlyu;"), | |
list("arstdhneio"), | |
list("zxcvbkm,./") | |
] | |
# (If you also want to include number/punctuation rows, you can define additional grids or expand these ones.) | |
layouts_grids = { | |
"qwerty": grid_qwerty, | |
"qwertz": grid_qwertz, | |
"azerty": grid_azerty, | |
"dvorak": grid_dvorak, | |
"colemak": grid_colemak | |
} | |
all_sequences = set() | |
for layout_name, grid in layouts_grids.items(): | |
seqs = generate_adjacent_sequences_from_grid(grid) | |
all_sequences.update(seqs) | |
# Escape any regex metacharacters in the sequences: | |
escaped_substrings = [re.escape(s) for s in all_sequences] | |
# Sort descending by length so longer alternatives are attempted first. | |
escaped_substrings.sort(key=len, reverse=True) | |
# Combine into one big alternation group. | |
combined_regex = "(?:" + "|".join(escaped_substrings) + ")" | |
# Print the final regex (it may be very long): | |
print("Combined regex for horizontal, vertical, and diagonal adjacencies:") | |
print(combined_regex) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment