fork download
  1. # Test de Majin
  2.  
  3.  
  4. import unittest
  5.  
  6. #from majin import Ficha
  7. #from majin import FichaAS
  8. #from majin import Jugador
  9. #from majin import JugadorHumano
  10. #from majin import JugadorOrdenador
  11.  
  12.  
  13. # Majin
  14.  
  15. """Para un juego de dominó basado en el Mahjong, el Majín,
  16. es necesario crear objetos Ficha con doble valor facial (iz izquierdo y dr derecho)
  17. del 1 al 9.
  18. Internamente se guardan los dos valores como una tupla,
  19. accesible mediante las propiedades valor (devuelve la tupla entera),
  20. iz (el primer valor) y dr (el segundo valor).
  21. El método __str_() devuelve los valores con el formato “[<iz>|<dr>]”.
  22. El método encaje_con(par: tuple[Ficha]) devuelve a) ENCAJE_NULO,
  23. b) ENCAJE_PARCIAL o c) ENCAJE_TOTAL según la ficha
  24. a) no contenga ninguno de los valores de las fichas en el par,
  25. b) contenga uno de los valores de las fichas en el par,
  26. o c) contenga valores de las dos fichas en el par.
  27. Por ejemplo, dadas las fichas f0 = (1, 1), f1 = (4, 5), f2 = (9, 4), f3 = (5, 9)
  28. y f4 = (4, 4), la ficha f0 no encaja con ninguna otra tomadas en pares,
  29. la ficha f4 encaja parcialmente con f2 y f3,
  30. y la ficha f1 encaja totalmente con f2 y f3.
  31. """
  32. class Ficha:
  33. ENCAJE_NULO = 1111
  34. ENCAJE_PARCIAL = 1112
  35. ENCAJE_TOTAL = 1113
  36.  
  37. def __init__(self, iz: int, dr: int):
  38. if iz < 0 or iz > 9:
  39. raise ValueError("iz fuera de rango (0-9): " + str(iz))
  40.  
  41. if dr < 0 or dr > 9:
  42. raise ValueError("dr fuera de rango (0-9): " + str(dr))
  43.  
  44. self.__valor = (iz, dr)
  45.  
  46. @property
  47. def valor(self):
  48. return self.__valor
  49.  
  50. @property
  51. def iz(self):
  52. return self.valor[0]
  53.  
  54. @property
  55. def dr(self):
  56. return self.valor[1]
  57.  
  58. def encaje_con(self, par: tuple["Ficha"]) -> int:
  59. toret = Ficha.ENCAJE_NULO
  60.  
  61. for ficha in par:
  62. if (self.iz == ficha.iz
  63. or self.dr == ficha.dr):
  64. toret = Ficha.ENCAJE_PARCIAL if toret == Ficha.ENCAJE_NULO else Ficha.ENCAJE_TOTAL
  65.  
  66. return toret
  67.  
  68. def __str__(self):
  69. return f"[{self.iz}|{self.dr}]"
  70.  
  71.  
  72. """La clase FichaAS representa a una ficha que puede encajar con cualquier otra,
  73. cuya representación textual es “[*|*]”.
  74. Internamente, los valores de esta ficha son (0, 0).
  75. """
  76. class FichaAS(Ficha):
  77. def __init__(self):
  78. super().__init__(0, 0)
  79.  
  80. def encaje_con(self, par: tuple["Ficha"]) -> int:
  81. return Ficha.ENCAJE_TOTAL
  82.  
  83. def __str__(self):
  84. return "[*|*]"
  85.  
  86.  
  87. """Escriba (al menos), las clases JugadorHumano y JugadorOrdenador.
  88. Ambas clases guardan un nombre
  89. y una colección doble de fichas
  90. (una lista y un diccionario sobre las mismas fichas),
  91. que son las que el jugador posee.
  92. Ambas tienen un método admite(fichas: lista[Ficha])
  93. que añade las fichas dadas a las colecciones de fichas del jugador,
  94. una propiedad de solo lectura para devolver nombre y un método __str_()
  95. que devuelve “<nombre>: <ficha1>, <ficha2> [, … <fichan>]”.
  96. Ambas tienen un método fichas_con(i: int) - > tuple[Ficha],
  97. que simplifica el mostrar agrupadas al jugador las fichas
  98. que tuvieran un determinado valor i en alguno de sus dos extremos.
  99. El jugador ordenador siempre se llama Ralph^,
  100. y si por ejemplo tuviese las fichas (1, 3), (4, 2) y (9, 6),
  101. su método __str_() devolvería “Ralph^: [1|3] [4|2] [9|6]”.
  102. Además, ambas clases tienen el método
  103. haz_jugada(self, par: tuple[Ficha]) -> Ficha”.
  104. El jugador humano simplemente devuelve la jugada almacenada mediante
  105. la propiedad de lectura y escritura jugada, que acepta
  106. y devuelve un objeto Ficha.
  107. En la clase JugadorOrdenador, el método haz_jugada()
  108. busca una ficha que haga encaje total,
  109. en caso de no encontrarla devuelve una ficha que haga encaje parcial,
  110. o devuelve None."""
  111. class Jugador:
  112. def __init__(self, nombre: str):
  113. self.__nombre = nombre
  114. self.__fichas = []
  115. self.__dict_fichas = {0: [], 1: [], 2:[], 3:[], 4:[],
  116. 5:[], 6:[], 7:[], 8:[], 9:[]}
  117.  
  118. @property
  119. def nombre(self):
  120. return self.__nombre
  121.  
  122. @property
  123. def fichas(self):
  124. return list(self.__fichas)
  125.  
  126. def admite(self, fichas: list[Ficha]):
  127. self.__fichas += fichas
  128. for f in fichas:
  129. self.__dict_fichas[f.iz].append(f)
  130. self.__dict_fichas[f.dr].append(f)
  131.  
  132. def fichas_con(self, i: int) -> tuple[Ficha]:
  133. return tuple(self.__dict_fichas[i])
  134.  
  135. def __str__(self):
  136. return f"{self.nombre}: {str.join(' ', [str(x) for x in self.fichas])}"
  137.  
  138.  
  139. """Al jugador humano se le pasa la jugada."""
  140. class JugadorHumano(Jugador):
  141. def __init__(self, nombre: str):
  142. super().__init__(nombre)
  143. self.__jugada: Ficha| None = None
  144.  
  145. @property
  146. def jugada(self) -> Ficha|None:
  147. return self.__jugada
  148.  
  149. @jugada.setter
  150. def jugada(self, f:Ficha):
  151. self.__jugada = f
  152.  
  153. def haz_jugada(self, otro_par: tuple[Ficha]) -> Ficha|None:
  154. return self.__jugada
  155.  
  156.  
  157. """El jugador ordenador deduce la jugada."""
  158. class JugadorOrdenador(Jugador):
  159. def __init__(self):
  160. super().__init__("Ralph^")
  161.  
  162. def haz_jugada(self, otro_par: tuple[Ficha]) -> Ficha|None:
  163. toret = None
  164.  
  165. for ficha in self.fichas:
  166. if ficha.encaje_con(otro_par) == Ficha.ENCAJE_TOTAL:
  167. toret = ficha
  168. break
  169. elif ficha.encaje_con(otro_par) == Ficha.ENCAJE_PARCIAL:
  170. toret = ficha
  171.  
  172. return toret
  173.  
  174.  
  175. class TestFicha(unittest.TestCase):
  176. def test_crea_ficha(self):
  177. f = Ficha(1, 5)
  178. self.assertEqual((1, 5), f.valor)
  179. self.assertEqual(1, f.iz)
  180. self.assertEqual(5, f.dr)
  181. self.assertEqual("[1|5]", str(f))
  182. self.assertNotEqual(5, f.iz)
  183. self.assertNotEqual(1, f.dr)
  184. self.assertNotEqual("[5|1]", str(f))
  185.  
  186. #self.assertRaises(ValueError, lambda: Ficha(-1, 5))
  187. #self.assertRaises(ValueError, lambda: Ficha(9, -5))
  188. #self.assertRaises(ValueError, lambda: Ficha(9, 11))
  189. #self.assertRaises(ValueError, lambda: Ficha(11, 5))
  190.  
  191. def test_encaje(self):
  192. f1 = Ficha(1, 6)
  193. f2 = Ficha(9, 9)
  194. f3 = Ficha(5, 6)
  195. f4 = Ficha(1, 6)
  196.  
  197. self.assertEqual(Ficha.ENCAJE_NULO, f2.encaje_con((f1, f3)))
  198. self.assertEqual(Ficha.ENCAJE_PARCIAL, f1.encaje_con((f2, f3)))
  199. self.assertEqual(Ficha.ENCAJE_TOTAL, f1.encaje_con((f4, f3)))
  200.  
  201. def test_crea_as(self):
  202. f1 = Ficha(1, 5)
  203. f2 = Ficha(9, 9)
  204. f3 = Ficha(5, 6)
  205. fas = FichaAS()
  206.  
  207. self.assertEqual("[*|*]", str(fas))
  208. self.assertEqual(Ficha.ENCAJE_TOTAL, fas.encaje_con((f1, f2)))
  209. self.assertEqual(Ficha.ENCAJE_TOTAL, fas.encaje_con((f3, f1)))
  210.  
  211.  
  212. class TestJugador(unittest.TestCase):
  213. def setUp(self):
  214. self.__fichas = [Ficha(1, 2), Ficha(3, 4), Ficha(5, 6)]
  215.  
  216. def test_crea_jugador(self):
  217. j = Jugador("abstract")
  218. j.admite(self.__fichas)
  219. self.assertEqual("abstract", j.nombre)
  220. self.assertEqual(self.__fichas, j.fichas)
  221. self.assertEqual("abstract: [1|2] [3|4] [5|6]", str(j))
  222.  
  223. def test_jugador_fichas_con(self):
  224. j = Jugador("abstract")
  225. j.admite(self.__fichas)
  226.  
  227. self.assertEqual("[1|2]", str(j.fichas_con(1)[0]))
  228. self.assertEqual("[1|2]", str(j.fichas_con(2)[0]))
  229. self.assertEqual("[3|4]", str(j.fichas_con(3)[0]))
  230. self.assertEqual("[3|4]", str(j.fichas_con(4)[0]))
  231. self.assertEqual("[5|6]", str(j.fichas_con(5)[0]))
  232. self.assertEqual("[5|6]", str(j.fichas_con(6)[0]))
  233.  
  234.  
  235. def test_jugada_jugador_humano(self):
  236. jh1 = JugadorHumano("jh1")
  237. jh1.admite(self.__fichas)
  238. self.assertEqual("jh1", jh1.nombre)
  239. self.assertEqual(self.__fichas, jh1.fichas)
  240. self.assertEqual("jh1: [1|2] [3|4] [5|6]", str(jh1))
  241.  
  242. f = Ficha(9, 9)
  243. jh1.jugada = f
  244. self.assertEqual(f, jh1.jugada)
  245. self.assertEqual(f, jh1.haz_jugada((None, None)))
  246.  
  247. def test_jugada_jugador_ordenador(self):
  248. jo1 = JugadorOrdenador()
  249. jo1.admite(self.__fichas)
  250. self.assertEqual(self.__fichas, jo1.fichas)
  251. self.assertEqual("Ralph^: [1|2] [3|4] [5|6]", str(jo1))
  252.  
  253. # No match
  254. other_pieces = (Ficha(8, 9), Ficha(7, 8))
  255. f = jo1.haz_jugada(other_pieces)
  256. self.assertEqual(None, f)
  257.  
  258. # Partial match
  259. other_pieces = (Ficha(3, 9), Ficha(5, 7))
  260. f = jo1.haz_jugada(other_pieces)
  261. self.assertNotEqual("[1|2]", str(f))
  262.  
  263. # Total match
  264. other_pieces = (Ficha(3, 9), Ficha(4, 7))
  265. f = jo1.haz_jugada(other_pieces)
  266. self.assertEqual("[3|4]", str(f))
  267.  
  268.  
  269. if __name__ == "__main__":
  270. unittest.main()
  271.  
  272.  
Runtime error #stdin #stdout #stderr 0.15s 26772KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
Traceback (most recent call last):
  File "./prog.py", line 140, in <module>
  File "./prog.py", line 146, in JugadorHumano
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'