hand.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. '''
  2. * This file is part of the Chinchon (https://notabug.org/alkeon/chinchon.
  3. * Copyright (c) 2020 Alejandro "alkeon" Castilla.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, version 3.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. '''
  17. from card import Card
  18. from copy import deepcopy
  19. def to_number(card):
  20. return card.get_number()
  21. class Hand:
  22. def __init__(self, provided_list = None): #Default values are static
  23. if provided_list is None: provided_list = list()
  24. self.cards = provided_list
  25. def size(self):
  26. return len(self.cards)
  27. def set_cards(self, provided_list):
  28. self.cards = provided_list
  29. def sort(self):
  30. self.cards.sort(key = to_number)
  31. '''
  32. Calculate points of hand.
  33. Because of the size of the hand (7), as a maximum there is some limits:
  34. - Two three of a kind (or one three of a kind and four of a kind)
  35. - Two straights
  36. - One straight and two three (or four) of a kind
  37. First it checks all options for every possibility.
  38. I couldn't make it better so I decided to check it with three greedy algorithm.
  39. One that checks three (or four) of a kind
  40. Other that checks straights
  41. Other that checks both
  42. Choice with more points is saved.
  43. Gets all connected cards with every option and later gets not connected cards
  44. for best choice, and these points are returned.
  45. '''
  46. def calculate_points(self):
  47. min_points = 70
  48. position = 0
  49. straight = list()
  50. three_of_a_kind = list()
  51. is_chinchon = False
  52. three_of_a_kind_options = list()
  53. straight_options = list()
  54. sol = list()
  55. # Get all options from combinations
  56. while(position < len(self.cards)):
  57. three_of_a_kind_cards = self.detect_three_and_four_of_a_kind()
  58. if(len(three_of_a_kind_cards) > 2 and three_of_a_kind_cards not in three_of_a_kind_options):
  59. three_of_a_kind_options.append(three_of_a_kind_cards)
  60. #Insert all possible three of a kind from a four of a kind
  61. #This allows three of a kind when four aren't possible
  62. if(len(three_of_a_kind_cards) == 4):
  63. i = 0
  64. while(i < 4):
  65. four_to_three_of_a_kind = deepcopy(three_of_a_kind_cards)
  66. four_to_three_of_a_kind.pop(i)
  67. if(four_to_three_of_a_kind not in three_of_a_kind_options):
  68. three_of_a_kind_options.append(four_to_three_of_a_kind)
  69. i += 1
  70. not_straight_cards, chinchon = self.get_straight(position)
  71. if(len(not_straight_cards) > 2 and not_straight_cards not in straight_options):
  72. straight_options.append(not_straight_cards)
  73. position += 1
  74. if(chinchon):
  75. is_chinchon = True
  76. # Check three of a kind and four of a kind
  77. i = 0
  78. points = 0
  79. while(i < len(three_of_a_kind_options)):
  80. possible_solution = list()
  81. temp = 0
  82. if(i < len(three_of_a_kind_options) and self.can_be_added(possible_solution, three_of_a_kind_options[i])):
  83. possible_solution += three_of_a_kind_options[i]
  84. if(i + 1 < len(three_of_a_kind_options)):
  85. if(self.can_be_added(possible_solution, three_of_a_kind_options[i + 1])):
  86. possible_solution += three_of_a_kind_options[i + 1]
  87. if(i + 2 < len(three_of_a_kind_options)):
  88. if(self.can_be_added(possible_solution, three_of_a_kind_options[i + 2])):
  89. possible_solution += three_of_a_kind_options[i + 2]
  90. temp = self.get_points(possible_solution)
  91. if(temp > points):
  92. sol = deepcopy(possible_solution)
  93. points = temp
  94. i += 1
  95. # Check straight options
  96. i = 0
  97. while(i < len(straight_options)):
  98. possible_solution = list()
  99. if(i < len(straight_options) and self.can_be_added(possible_solution, straight_options[i])):
  100. possible_solution += straight_options[i]
  101. if(i + 1 < len(straight_options)):
  102. if(self.can_be_added(possible_solution, straight_options[i + 1])):
  103. possible_solution += straight_options[i + 1]
  104. if(i + 2 < len(straight_options)):
  105. if(self.can_be_added(possible_solution, straight_options[i + 2])):
  106. possible_solution += straight_options[i + 2]
  107. temp = self.get_points(possible_solution)
  108. if(temp > points):
  109. sol = deepcopy(possible_solution)
  110. points = temp
  111. i += 1
  112. # Check straight options and three and four of a kind
  113. i = 0
  114. while(i < len(straight_options)):
  115. e = 0
  116. while(e < len(three_of_a_kind_options)):
  117. possible_solution = list()
  118. possible_solution += straight_options[i]
  119. if(e < len(three_of_a_kind_options) and self.can_be_added(possible_solution, three_of_a_kind_options[e])):
  120. possible_solution += three_of_a_kind_options[e]
  121. if(e + 1 < len(three_of_a_kind_options)):
  122. if(self.can_be_added(possible_solution, three_of_a_kind_options[e + 1])):
  123. possible_solution += three_of_a_kind_options[e + 1]
  124. if(e + 2 < len(three_of_a_kind_options)):
  125. if(self.can_be_added(possible_solution, three_of_a_kind_options[e + 2])):
  126. possible_solution += three_of_a_kind_options[e + 2]
  127. temp = self.get_points(possible_solution)
  128. if(temp > points):
  129. sol = deepcopy(possible_solution)
  130. points = temp
  131. e += 1
  132. i += 1
  133. not_connected_cards = self.get_not_connected_cards(sol)
  134. min_points = self.get_points(not_connected_cards)
  135. if(min_points == 0 and is_chinchon == True):
  136. min_points = -100
  137. elif(min_points == 0):
  138. min_points = -10
  139. return min_points
  140. '''
  141. From connected cards, checks hand for not connected cards
  142. '''
  143. def get_not_connected_cards(self, connected):
  144. i = 0
  145. not_connected_cards = list()
  146. while(i < len(self.cards)):
  147. e = 0
  148. found = False
  149. while(e < len(connected)):
  150. if(self.cards[i].get_number() == connected[e].get_number()
  151. and self.cards[i].get_suit() == connected[e].get_suit()):
  152. found = True
  153. e += 1
  154. if(not found):
  155. not_connected_cards.append(self.cards[i])
  156. i += 1
  157. return not_connected_cards
  158. '''
  159. Checks if option can be added to solution
  160. '''
  161. def can_be_added(self, sol, cards_to_be_append):
  162. i = 0
  163. added = True
  164. while(i < len(self.cards)):
  165. if(self.cards[i] in sol and self.cards[i] in cards_to_be_append):
  166. added = False
  167. i += 1
  168. return added
  169. '''
  170. Takes card from position and check if the rest of the cards can be used for a straight
  171. also sends chinchon true if hand is a full straight
  172. '''
  173. def get_straight(self, position):
  174. possible_straight = list()
  175. first_card = self.cards[position]
  176. possible_straight = self.get_cards_same_suit(first_card.get_suit())
  177. chinchon = (len(possible_straight) == 7)
  178. possible_straight.sort(key = to_number)
  179. straight_cards = self.check_straight(possible_straight)
  180. chinchon = chinchon and (len(straight_cards) == 7)
  181. return straight_cards, chinchon
  182. '''
  183. Verify if cards is a valid straight
  184. '''
  185. def check_straight(self, possible_straight):
  186. position = 0
  187. return_straighted_cards = list()
  188. continuous_straight = 0
  189. not_straight_cards = list()
  190. straighted_cards = list()
  191. while(position < len(possible_straight)):
  192. if(position + 1 < len(possible_straight)):
  193. if(to_number(possible_straight[position]) !=
  194. to_number(possible_straight[position + 1]) - 1):
  195. if(continuous_straight < 2):
  196. continuous_straight = 0
  197. if(to_number(possible_straight[position - 1]) + 1 !=
  198. to_number(possible_straight[position])):
  199. not_straight_cards.insert(0, possible_straight[position])
  200. else:
  201. straighted_cards.insert(0, possible_straight[position])
  202. else:
  203. straighted_cards.insert(0, possible_straight[position])
  204. continuous_straight += 1
  205. else:
  206. if(to_number(possible_straight[position - 1]) + 1 !=
  207. to_number(possible_straight[position])):
  208. not_straight_cards.insert(0, possible_straight[position])
  209. else:
  210. straighted_cards.insert(0, possible_straight[position])
  211. position += 1
  212. if(continuous_straight < 2):
  213. return_straighted_cards = list()
  214. else:
  215. position = 0
  216. while(position < len(self.cards)):
  217. if(self.cards[position] in straighted_cards and
  218. self.cards[position] not in return_straighted_cards):
  219. return_straighted_cards.insert(0, self.cards[position])
  220. position += 1
  221. return return_straighted_cards
  222. '''
  223. Checks every card from hand for a three (or four) of a kind
  224. '''
  225. def detect_three_and_four_of_a_kind(self):
  226. position = 0
  227. return_three_of_a_kind_cards = list()
  228. three_of_a_kind_cards = list()
  229. while(position < len(self.cards)):
  230. provided_card = self.cards[position]
  231. new_position = 0
  232. possible_three_of_a_kind_cards = list()
  233. while(new_position < len(self.cards)):
  234. card = self.cards[new_position]
  235. if(to_number(provided_card) == to_number(card)):
  236. possible_three_of_a_kind_cards.insert(0, card)
  237. new_position += 1
  238. if(len(possible_three_of_a_kind_cards) > 2):
  239. three_of_a_kind_cards += possible_three_of_a_kind_cards
  240. position += 1
  241. position = 0
  242. # Delete repeated cards
  243. while(position < len(self.cards)):
  244. if(self.cards[position] in three_of_a_kind_cards):
  245. return_three_of_a_kind_cards.insert(0, self.cards[position])
  246. position += 1
  247. return return_three_of_a_kind_cards
  248. def get_cards_same_suit(self, suit):
  249. position = 0
  250. same_suit_cards = list()
  251. while(position < len(self.cards)):
  252. card = self.cards[position]
  253. if(suit == card.get_suit()):
  254. same_suit_cards.insert(0, card)
  255. position += 1
  256. return same_suit_cards
  257. def insert(self, position, object):
  258. self.cards.insert(position, object)
  259. def pop(self, position):
  260. self.cards.pop(position)
  261. def get_cards(self):
  262. return self.cards.copy()
  263. '''
  264. Sum of all points from cards
  265. Every card adds its own number unless 11 and 12, those add 10 points.
  266. '''
  267. def get_points(self, cards):
  268. position = 0
  269. points = 0
  270. set_seen = set()
  271. while(position < len(cards)):
  272. if(cards[position] not in set_seen):
  273. first_number = to_number(cards[position])
  274. if(first_number < 11):
  275. points += first_number
  276. else:
  277. points += 10
  278. set_seen.add(cards[position])
  279. position += 1
  280. return points
  281. def get(self, position):
  282. return self.cards[position]
  283. def __str__(self):
  284. i = 0
  285. string_result = str()
  286. while(i < self.size()):
  287. string_result += str(i) + ".- " + str(self.cards[i]) + " "
  288. i += 1
  289. return string_result