mirror of
https://github.com/Rushilwiz/AI.git
synced 2025-04-09 22:10:17 -04:00
257 lines
8.8 KiB
Python
257 lines
8.8 KiB
Python
import sys; args = sys.argv[1:]
|
|
|
|
# Name: Rushil Umaretiya
|
|
# Date: 3-18-2021
|
|
|
|
import re
|
|
|
|
BLOCKCHAR = '#'
|
|
OPENCHAR = '-'
|
|
PROTECTEDCHAR = '~'
|
|
|
|
def crossword(args):
|
|
# parse input
|
|
# args = ['13x13', '32', 'dct20k.txt', 'H1x4#Toe#', 'H9x2#', 'V3x6#', 'H10x0Scintillating', 'V0x5stirrup', 'H4x2##Ordained', 'V0x1Sums', 'V0x12Mah', 'V5x0pew']
|
|
height, width, block_count, wordlist, words = parse_input(args)
|
|
|
|
# initialize board
|
|
board, width, height, block_count = initialize(width, height, words, block_count)
|
|
|
|
print("init board:")
|
|
display(board, width, height)
|
|
|
|
# add blocking chars
|
|
print("add blocks:")
|
|
board = add_blocks(board, width, height, block_count)
|
|
|
|
if board == None:
|
|
print('Board is none.')
|
|
raise Exception(f'Board is none.')
|
|
|
|
# display(board, width, height)
|
|
# remove border
|
|
board, width, height = finish_board(board, width, height, words)
|
|
|
|
#print(f'{block_count} {board.count(BLOCKCHAR)}')
|
|
print(board.count(BLOCKCHAR))
|
|
display(board, width, height)
|
|
|
|
def parse_input(args):
|
|
tests = [r'^(\d+)x(\d+)$', r'^\d+$', r'^(H|V)(\d+)x(\d+)(.+)$']
|
|
height, width, block_count, wordlist, words = 0, 0, 0, '', []
|
|
|
|
for arg in args:
|
|
# if os.path.isfile(arg):
|
|
# wordlist = arg
|
|
# else:
|
|
for i in range(len(tests)):
|
|
match = re.search(tests[i], arg, re.I)
|
|
if match == None: continue
|
|
if i == 0:
|
|
height = int(match.group(1))
|
|
width = int(match.group(2))
|
|
elif i == 1:
|
|
block_count = int(match.group(0))
|
|
elif i == 2:
|
|
words.append((match.group(1).upper(), int(match.group(2)), int(match.group(3)), match.group(4).upper()))
|
|
return height, width, block_count, wordlist, words
|
|
|
|
def initialize(width, height, words, block_count):
|
|
board = OPENCHAR * height * width
|
|
for word in words:
|
|
index = word[1] * width + word[2]
|
|
for letter in word[3]:
|
|
new_char = BLOCKCHAR if letter == BLOCKCHAR else PROTECTEDCHAR
|
|
board = board[:index] + new_char + board[index + 1 :]
|
|
board = board[:(len(board) - 1) - index] + new_char + board[len(board) - index:]
|
|
if word[0] == 'H':
|
|
index += 1
|
|
else:
|
|
index += width
|
|
block_count -= board.count(BLOCKCHAR)
|
|
display(board, width, height)
|
|
board = add_border(board, width, height)
|
|
width += 2
|
|
height += 2
|
|
# display(board, width, height)
|
|
board = protect(board, width, height)
|
|
# display(board, width, height)
|
|
return board, width, height, block_count
|
|
|
|
def protect(board, width, height):
|
|
right_test = rf'({BLOCKCHAR}(\w|{PROTECTEDCHAR})(\w|{PROTECTEDCHAR})){OPENCHAR}'
|
|
left_test = rf'{OPENCHAR}((\w|{PROTECTEDCHAR})(\w|{PROTECTEDCHAR}){BLOCKCHAR})'
|
|
|
|
for i in range(2):
|
|
board = re.sub(left_test, rf'{PROTECTEDCHAR}\1', board)
|
|
board = re.sub(right_test, rf'\1{PROTECTEDCHAR}', board)
|
|
board = transpose(board, width)
|
|
width, height = height, width
|
|
# display(board, width, height)
|
|
|
|
return board
|
|
|
|
def transpose(board, width):
|
|
return ''.join([board[col::width] for col in range(width)])
|
|
|
|
def add_border(board, width, height):
|
|
border_board = BLOCKCHAR*(width+3)
|
|
border_board +=(BLOCKCHAR*2).join([board[p:p+width] for p in range(0,len(board),width)])
|
|
border_board += BLOCKCHAR*(width+3)
|
|
return border_board
|
|
|
|
def remove_border(board, width, height):
|
|
no_border = ''
|
|
for i in range(len(board)):
|
|
if (width <= i < width * (height - 1)) and ((i + 1) % width != 0) and (i % width != 0):
|
|
no_border += board[i]
|
|
return no_border, width - 2, height - 2
|
|
|
|
def blocking_heuristic(index, board, width):
|
|
left = 0
|
|
temp = index
|
|
while board[temp] != BLOCKCHAR:
|
|
left += 1
|
|
temp += 1
|
|
right = 0
|
|
temp = index
|
|
while board[temp] != BLOCKCHAR:
|
|
right += 1
|
|
temp -= 1
|
|
up = 0
|
|
temp = index
|
|
while board[temp] != BLOCKCHAR:
|
|
up += 1
|
|
temp += width
|
|
down = 0
|
|
temp = index
|
|
while board[temp] != BLOCKCHAR:
|
|
down += 1
|
|
temp -= width
|
|
return up * down + left * right
|
|
|
|
def add_blocks(board, width, height, block_count):
|
|
if block_count == 0:
|
|
return board
|
|
|
|
if board.count(OPENCHAR) == block_count:
|
|
return BLOCKCHAR * len(board)
|
|
|
|
if block_count % 2 == 1:
|
|
if width * height % 2 == 1:
|
|
board = board[: len(board) // 2] + BLOCKCHAR + board[(len(board) // 2) + 1 :]
|
|
block_count -= 1
|
|
else:
|
|
raise Exception("Cannot place an odd number of blockchars on an even sized board.")
|
|
|
|
print(board)
|
|
if re.search(f'#[{PROTECTEDCHAR+OPENCHAR}]{{1,2}}#', board) or re.search(f'#[{PROTECTEDCHAR+OPENCHAR}]{{1,2}}#', transpose(board, width)):
|
|
|
|
display(board, width, height)
|
|
|
|
for i in range(2):
|
|
presub = board.count(BLOCKCHAR)
|
|
board, num = re.subn(f'#([{PROTECTEDCHAR+OPENCHAR}][{PROTECTEDCHAR+OPENCHAR}]#)*', lambda x: '#' * len(x.group()), board)
|
|
board, num = re.subn(f'#([{PROTECTEDCHAR+OPENCHAR}]#)*', lambda x: '#' * len(x.group()), board)
|
|
block_count -= board.count(BLOCKCHAR) - presub
|
|
|
|
board = transpose(board, width)
|
|
width, height = height, width
|
|
|
|
# print("yes.")
|
|
display(board, width, height)
|
|
# print("yes")
|
|
possible = [i for i in range(len(board)) if board[i] != BLOCKCHAR]
|
|
fills = {}
|
|
|
|
for i in possible:
|
|
fills[i] = area_fill(board, width, i)
|
|
|
|
fill_counts = {}
|
|
for fill in fills.keys():
|
|
count = fills[fill].count('?')
|
|
|
|
if count not in fill_counts.values():
|
|
fill_counts[fill] = count
|
|
|
|
fill_counts = {key: value for key, value in sorted(fill_counts.items(), key=lambda item: item[1])}
|
|
for fill in fill_counts:
|
|
if fill_counts[fill] < (width - 2) * (height - 2) - (board.count(PROTECTEDCHAR) + board.count(OPENCHAR)):
|
|
board = area_fill(board, width, fill, char=BLOCKCHAR)
|
|
board = area_fill(board, width, len(board) - 1 - fill, char=BLOCKCHAR)
|
|
block_count -= fill_counts[fill] * 2
|
|
break
|
|
|
|
options = [i for i in range(len(board)) if board[i] == board[(len(board) - 1) - i] == OPENCHAR]
|
|
return blocks_backtrack(board, width, height, block_count, options)
|
|
|
|
def blocks_backtrack(board, width, height, block_count, options):
|
|
# print(options)
|
|
# display(board, width, height)
|
|
|
|
if block_count == 0 or len(options) == 0:
|
|
return board
|
|
|
|
for option in sorted(options, key=lambda i: blocking_heuristic(i, board, width)):
|
|
if is_valid_blocking(board, width, height, option):
|
|
copy = board[:option] + BLOCKCHAR + board[option + 1 :]
|
|
copy = copy[: (len(copy) - 1) - option] + BLOCKCHAR + copy[len(copy) - option :]
|
|
updated_options = [i for i in options if i != option]
|
|
result = blocks_backtrack(copy, width, height, block_count - 2, updated_options)
|
|
if result != None: return result
|
|
|
|
return None
|
|
|
|
def is_valid_blocking(board, width, height, option):
|
|
if board[option] != OPENCHAR: return False
|
|
temp = board[:option] + BLOCKCHAR + board[option + 1:]
|
|
temp = temp[:(len(temp) - 1) - option] + BLOCKCHAR + temp[len(temp) - option :]
|
|
|
|
illegalRegex = rf"[{BLOCKCHAR}](.?({PROTECTEDCHAR}|{OPENCHAR})|({PROTECTEDCHAR}|{OPENCHAR}).?)[{BLOCKCHAR}]"
|
|
if re.search(illegalRegex, temp) != None: return False
|
|
if re.search(illegalRegex, transpose(temp, width)) != None: return False
|
|
return True
|
|
|
|
def area_fill(board, width, sp, char='?'):
|
|
dirs = [-1, width, 1, -1 * width]
|
|
if sp < 0 or sp >= len(board): return board
|
|
if board[sp] in (OPENCHAR, PROTECTEDCHAR):
|
|
board = board[0:sp] + char + board[sp+1:]
|
|
for d in dirs:
|
|
if d == -1 and sp % width == 0: continue
|
|
if d == 1 and sp + 1 % width == 0: continue
|
|
board = area_fill(board, width, sp + d, char)
|
|
return board
|
|
|
|
def finish_board(board, width, height, words):
|
|
# remove border
|
|
board, width, height = remove_border(board, width, height)
|
|
|
|
# add words
|
|
for word in words:
|
|
index = word[1] * width + word[2]
|
|
for letter in word[3]:
|
|
board = board[:index] + letter + board[index + 1 :]
|
|
if word[0] == 'H':
|
|
index += 1
|
|
else:
|
|
index += width
|
|
|
|
# replace protected with open
|
|
board = re.sub(PROTECTEDCHAR, OPENCHAR, board)
|
|
|
|
return board, width, height
|
|
|
|
def display(board, width, height):
|
|
for i in range(height):
|
|
line = ""
|
|
for letter in range(width):
|
|
line += (board[(i * width) + letter] + " ")
|
|
print(line)
|
|
print()
|
|
|
|
def main():
|
|
crossword(args)
|
|
|
|
if __name__ == '__main__':
|
|
main() |