Queer European MD passionate about IT

project.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. """Final project for CS50P - Davte. See README.md for more information."""
  2. import math
  3. import random
  4. from typing import Tuple
  5. import fpdf
  6. import fpdf.table
  7. from fpdf.fonts import FontFace
  8. from fpdf.enums import TextEmphasis
  9. BOLD = TextEmphasis.coerce('B')
  10. def draw_table(pdf: fpdf.FPDF, state: str, key_position: int = -1, coin_to_flip: int = -1,
  11. highlight_parity_bits: bool = False) -> fpdf.table.Table:
  12. square_side = int(len(state)**0.5)
  13. with pdf.table(text_align='CENTER',
  14. first_row_as_headings=False) as table:
  15. for i, v in enumerate(state):
  16. if i % square_side == 0:
  17. row = table.row()
  18. cell_value = {'0': 'H', '1': 'T'}[v]
  19. cell_style = FontFace()
  20. if i == coin_to_flip:
  21. cell_style.fill_color = (153, 0, 153)
  22. cell_style.emphasis = BOLD
  23. if i == key_position:
  24. cell_style.color = (255, 128, 0)
  25. cell_style.emphasis = BOLD
  26. elif highlight_parity_bits and i in (1, 2, 4, 8, 16, 32):
  27. cell_style.color = (119, 136, 153)
  28. cell_style.emphasis = BOLD
  29. row.cell(cell_value, style=cell_style)
  30. return table
  31. def get_parity(n: str) -> int:
  32. num_bits = int(math.log2(len(n)))
  33. parity = 0
  34. for i in range(num_bits):
  35. block_parity = 0
  36. for j, val in enumerate(n):
  37. if j & (2**i) == 2**i:
  38. block_parity = block_parity ^ int(val)
  39. parity += block_parity * (2**i)
  40. return parity
  41. def get_coin_to_flip(initial_state: str, key_position: int) -> int:
  42. current_value = get_parity(initial_state)
  43. return current_value ^ key_position
  44. def store_pdf(file_name, state, key_position: int = -1, coin_to_flip: int = -1):
  45. pdf = fpdf.FPDF(orientation="P", unit="mm", format="A4")
  46. pdf.set_auto_page_break(False)
  47. pdf.add_page()
  48. pdf.set_font("Times", size=100 // math.log2(math.sqrt(len(state))))
  49. draw_table(pdf=pdf, state=state, key_position=key_position, coin_to_flip=coin_to_flip)
  50. pdf.output(file_name)
  51. def solve(initial_state: str, coin_to_flip: int) -> str:
  52. """Return the chessboard in `initial_state` after flipping `coin_to_flip`."""
  53. result = list(initial_state)
  54. result[coin_to_flip] = '0' if result[coin_to_flip] == '1' else '1'
  55. return ''.join(result)
  56. def is_power_of_two(n: int) -> bool:
  57. k = 1
  58. while k <= n:
  59. if k == n:
  60. return True
  61. k *= 2
  62. return False
  63. def get_parameters(board_side: int = 8) -> Tuple[str, int, int]:
  64. """Generate a random chessboard and a random key position and solve the puzzle.
  65. Return a board of side `board_side`, a key position and the coin to flip.
  66. """
  67. if not is_power_of_two(board_side):
  68. raise ValueError("Board side must be a power of two!")
  69. random.seed()
  70. initial_state = ''.join(map(str, (random.randint(0, 1) for _ in range(board_side ** 2))))
  71. key_position = random.randint(0, board_side ** 2 - 1)
  72. coin_to_flip = get_coin_to_flip(initial_state, key_position)
  73. return initial_state, key_position, coin_to_flip
  74. def main() -> None:
  75. board_side = 0
  76. while board_side < 2:
  77. try:
  78. board_side = input("Choose a side length for the chessboard (press enter for default value 8)\t\t")
  79. if not board_side:
  80. board_side = 8
  81. board_side = int(board_side)
  82. if not is_power_of_two(board_side):
  83. raise ValueError
  84. except (ValueError, TypeError):
  85. board_side = 0
  86. print(f"Invalid input `{board_side}`. Please enter a power of two.")
  87. continue
  88. except KeyboardInterrupt:
  89. print("\nExiting...")
  90. return
  91. print(f"Generating a random {board_side} x {board_side} chessboard...")
  92. initial_state, key_position, coin_to_flip = get_parameters(board_side=board_side)
  93. print("Show Player 1 the file `Key.pdf`.")
  94. store_pdf(file_name='Key.pdf', state=initial_state,
  95. key_position=key_position)
  96. final_state = solve(initial_state, coin_to_flip)
  97. print("Once Player 1 has flipped a coin, the chessboard should look like "
  98. "the one in `Problem.pdf`. Show it to Player 2.")
  99. store_pdf(file_name='Problem.pdf', state=final_state)
  100. print("You can use `Solution.pdf` to validate the answer of Player 2.")
  101. store_pdf(file_name='Solution.pdf', state=final_state,
  102. key_position=key_position, coin_to_flip=coin_to_flip)
  103. if __name__ == '__main__':
  104. main()