PY-2017.3 <Brad@Libra Merge branch 'master'

Conflicts:
	_windows/laf.xml
	editor.xml
This commit is contained in:
bradstein 2018-01-10 23:51:33 -06:00
commit 3d8cf58e5a
48 changed files with 326 additions and 350 deletions

View File

@ -11,11 +11,10 @@ Things done:
6) Input safety
Things to do:
1) Make end of game conditions match actual end of game conditions
2) Testing classes
3) Turn Timer
4) Django server and hosting
5) HTML5 frontend
6) Multiplayer
7) Remaining 2nd Edition Base Cards: Bureaucrat, Chapel, Feast, Laboratory, Moneylender, Throne Room, Council Room, Festival, Library, Harbringer, Vassal, Gardens, Poacher, Bandit, Witch, Artisan, & Sentry
8) Choosing play sets
1) Testing classes
2) Turn Timer
3) Django server and hosting
4) HTML5 frontend
5) Multiplayer
6) Remaining 2nd Edition Base Cards: Bureaucrat, Chapel, Feast, Laboratory, Moneylender, Throne Room, Council Room, Festival, Library, Harbringer, Vassal, Gardens, Poacher, Bandit, Witch, Artisan, & Sentry
7) Choosing play sets

View File

@ -1,5 +0,0 @@
<application>
<component name="Git.Application.Settings">
<option name="SSH_EXECUTABLE" value="IDEA_SSH" />
</component>
</application>

View File

@ -1,5 +1,5 @@
<application>
<component name="LafManager">
<laf class-name="com.intellij.ide.ui.laf.darcula.DarculaLaf" />
<laf class-name="com.intellij.ide.ui.laf.IntelliJLaf" />
</component>
</application>

View File

@ -0,0 +1,5 @@
from card.basic.card_kingdom import Kingdom
class Action(Kingdom):
pass

11
card/basic/card_attack.py Normal file
View File

@ -0,0 +1,11 @@
from card.basic.card_kingdom import Kingdom
class Attack(Kingdom):
def effect(self):
for player in self.get_owner().get_table().get_players():
if self.get_owner() != player and not player.get_hand().reaction_blocks_attack(self.get_name()):
self.attack(player)
def attack(self, player):
pass

10
card/basic/card_curse.py Normal file
View File

@ -0,0 +1,10 @@
from card.basic.card_victory import Victory
class Curse(Victory):
@classmethod
def pile_setup(cls, player_count):
if player_count % cls.normal_full_table < cls.normal_full_table/2:
return Victory.pile_player_rate
else:
return (player_count - 1) * cls.pile_player_rate

View File

@ -0,0 +1,10 @@
from card.card import Card
from math import floor
class Kingdom(Card):
pile_player_rate = 10
@classmethod
def pile_setup(cls, player_count):
return (floor(player_count/cls.normal_full_table) + 1) * cls.pile_player_rate

View File

@ -0,0 +1,5 @@
from card.basic.card_kingdom import Kingdom
class Reaction(Kingdom):
pass

View File

@ -0,0 +1,8 @@
from card.card import Card
from math import floor
class Treasure(Card):
@classmethod
def pile_setup(cls, player_count):
return (floor(player_count/cls.normal_full_table) + 1) * cls.pile_player_rate

View File

@ -0,0 +1,21 @@
from card.card import Card
from math import floor
class Victory(Card):
two_player_count = 8
four_player_count = 12
five_player_count = 15
six_player_count = 18
@classmethod
def pile_setup(cls, player_count):
if 0 < player_count % Card.normal_full_table < Card.normal_full_table/2:
supplement = cls.two_player_count
elif Card.normal_full_table/2 <= player_count % Card.normal_full_table < Card.normal_full_table - 1:
supplement = cls.four_player_count
elif player_count % Card.normal_full_table == Card.normal_full_table - 1:
supplement = cls.five_player_count
else:
supplement = cls.six_player_count
return (floor(player_count/Card.normal_full_table) * cls.six_player_count) + supplement

View File

@ -1,22 +1,11 @@
from enum import Enum, auto
class Card:
class CardType(Enum):
Treasure = auto()
Action = auto()
Reaction = auto()
Attack = auto()
Victory = auto()
Curse = auto()
normal_full_table = 6
pile_player_rate = 10
prevent_attack = False
def __init__(self, name, cost, cardtype, value, coin, action, buy, draw, owner):
def __init__(self, name, cost, value, coin, action, buy, draw, owner):
self.__name = name
self.__cost = cost
self.__coin = coin
self.__type = cardtype
self.__action = action
self.__buy = buy
self.__draw = draw
@ -34,16 +23,22 @@ class Card:
# This is here so that 'special' card can override this function so that unique card effects can happen.
pass
def setup(self):
def react(self, what_attack):
return False
@classmethod
def pile_setup(cls, player_count):
# This is here so that each card can override this function so that the right number of .
pass
@staticmethod
def setup():
# This is here so that 'special' card can override this function so that unique card setup effects can happen.
pass
def get_name(self):
return self.__name
def get_type(self):
return self.__type
def get_points(self):
return self.__value
@ -60,7 +55,15 @@ class Card:
return self.__owner
def identify(self):
return self.__name + ", " + str(self.__type) + ", " + str(self.__cost)
return self.__name + ", " + self.__str__() + ", costing " + str(self.__cost)
def print_card_list(self, card, message):
print("\nPlayer " + str(self._Card__owner.get_player_index()) + " " + message)
counter = 0
for c in card:
print(str(counter) + ": " + c.identify())
counter += 1
def __get_index_not_self(self):
result = -1
@ -69,13 +72,5 @@ class Card:
result = self._Card__owner.get_hand().get_player_index()
return result
def __print_card_list(self, card, message):
print("\nPlayer " + str(self._Card__owner.get_player_index()) + " " + message)
counter = 0
for c in card:
print(str(counter) + ": " + c.identify())
counter += 1
def __str__(self):
return "A " + self.__name + " card."
return "a " + self.__name + " card"

View File

@ -1,8 +0,0 @@
from card.card import Card
from card.card_gain_trash import CardGainTrash
class Mine(CardGainTrash):
coin_gain = 3
trashable_type_restriction = [Card.CardType.Treasure]
gainable_type_restriction = [Card.CardType.Treasure]

View File

@ -1,5 +0,0 @@
from card.card import Card
class Moat(Card):
prevent_attack = True

View File

@ -1,7 +1,7 @@
from card.card import Card
from card.basic.card_action import Action
class Cellar(Card):
class Cellar(Action):
def effect(self):
hand_index = 0
cards_discarded = 0

5
card/named/copper.py Normal file
View File

@ -0,0 +1,5 @@
from card.basic.card_treasure import Treasure
class Copper(Treasure):
pile_player_rate = 60

8
card/named/estate.py Normal file
View File

@ -0,0 +1,8 @@
from card.basic.card_victory import Victory
class Estate(Victory):
two_player_count = 14
four_player_count = 18
five_player_count = 21
six_player_count = 24

5
card/named/gold.py Normal file
View File

@ -0,0 +1,5 @@
from card.basic.card_treasure import Treasure
class Gold(Treasure):
pile_player_rate = 30

View File

@ -1,7 +1,7 @@
from card.card import Card
from card.basic.card_action import Action
class Merchant(Card):
class Merchant(Action):
def effect(self):
silver_card_index = self._Card__owner.get_hand().get_index_of_card_by_name("Silver")
if silver_card_index >= 0:

View File

@ -1,14 +1,13 @@
from card.card import Card
from card.basic.card_attack import Attack
from card.basic.card_action import Action
from random import randint
class Militia(Card):
def effect(self):
for player in self._Card__owner.get_table().get_players():
if self._Card__owner != player and not player.get_hand().blocks_attack(self.get_name()):
player.print_hand()
print("Player " + str(player.get_player_index()) + ", you MUST discard down to 3 card.")
self.__force_discard(self._Card__owner.get_std_chances(), player)
class Militia(Action, Attack):
def attack(self, player):
player.print_hand()
print("Player " + str(player.get_player_index()) + ", you MUST discard down to 3 card.")
self.__force_discard(self.get_owner().get_std_chances(), player)
def __force_discard(self, chances, player):
if player.get_hand().get_remaining() > 3 and chances > 0:
@ -16,16 +15,17 @@ class Militia(Card):
" discard (0 to " + str(player.get_hand().get_remaining() - 1) + "): "
, int)
self.__check_discard(hand_index, player, chances)
elif self._Card__owner.get_hand().get_remaining() > 3 and chances <= 0:
elif self.get_owner().get_hand().get_remaining() > 3 and chances <= 0:
print("You're out of chances to select a valid card to discard, randomly selecting for you.")
player.discard_from_hand(randint(0, self.__hand.get_remaining() - 1))
player.discard_from_hand(randint(0, self.get_owner().get_hand().get_remaining() - 1))
def __check_discard(self, index, player, chances):
if 0 > index or index >= self._Card__owner.get_hand().get_remaining():
print("Valid inputs range from 0 to " + str(player.get_hand().get_remaining() - 1) + ". 1 chance lost.")
if 0 > index >= player.get_hand().get_remaining():
print("Valid inputs range from 0 to " + str(player.get_hand().get_remaining() - 1) + ". " + str(chances - 1)
+ "chances to input a valid index.")
self.__force_discard(chances - 1, player)
else:
print("Discarding " + player.get_hand().get_card(index).get_name() + ".")
player.discard_from_hand(index)
player.print_hand()
self.__force_discard(self._Card__owner.get_std_chances(), player)
self.__force_discard(self.get_owner().get_std_chances(), player)

9
card/named/mine.py Normal file
View File

@ -0,0 +1,9 @@
from card.basic.card_action import Action
from card.basic.card_treasure import Treasure
from card.special.card_gain_trash import CardGainTrash
class Mine(Action, CardGainTrash):
coin_gain = 3
trashable_type_restriction = Treasure
gainable_type_restriction = Treasure

10
card/named/moat.py Normal file
View File

@ -0,0 +1,10 @@
from card.basic.card_action import Action
from card.basic.card_reaction import Reaction
class Moat(Action, Reaction):
def react(self, what_attack):
owner = self.get_owner()
return "Y" == owner.get_general_input("Player " + str(owner.get_player_index()) + ", enter 'Y' if you'd "
"like to reveal " + str(self) + " to block the " + str(what_attack) +
" attack: ", str)

5
card/named/province.py Normal file
View File

@ -0,0 +1,5 @@
from card.basic.card_victory import Victory
class Province(Victory):
pass

6
card/named/remodel.py Normal file
View File

@ -0,0 +1,6 @@
from card.basic.card_action import Action
from card.special.card_gain_trash import CardGainTrash
class Remodel(Action, CardGainTrash):
coin_gain = 2

6
card/named/silver.py Normal file
View File

@ -0,0 +1,6 @@
from card.basic.card_treasure import Treasure
class Silver(Treasure):
pile_player_rate = 40

9
card/named/workshop.py Normal file
View File

@ -0,0 +1,9 @@
from card.basic.card_action import Action
from card.special.card_gain import CardGain
class Workshop(Action, CardGain):
coin_gain = 4
def effect(self):
self.gain_card(self.coin_gain)

View File

@ -1,5 +0,0 @@
from card.card_gain_trash import CardGainTrash
class Remodel(CardGainTrash):
coin_gain = 2

View File

@ -6,9 +6,9 @@ class CardGain(Card):
def gain_card(self, spending_limit):
gainable_cards = self.__get_gainable_cards(spending_limit)
self._Card__print_card_list(gainable_cards, "Gainable Cards: ")
self.print_card_list(gainable_cards, "Gainable Cards: ")
index = 0
chances = self._Card__owner.get_std_chances()
chances = self.get_owner().get_std_chances()
while len(gainable_cards) > 0 and 0 <= index < len(gainable_cards) - 1 and chances > 0:
index = self.__get_gain_card()
@ -18,24 +18,24 @@ class CardGain(Card):
index = 0
chances -= 1
else:
pile_index = self._Card__owner.get_table().get_pile_index_of_card(gainable_cards[index].get_name())
print("Player " + str(self._Card__owner.get_player_index()) + " drawing "
+ self._Card__owner.get_table().get_pile(pile_index).get_card_group().get_name() + " to hand.")
self._Card__owner.get_table().get_pile(pile_index).transfer_top_card(self._Card__owner.get_hand())
self._Card__owner.claim_top_card(self._Card__owner.get_hand())
pile_index = self.get_owner().get_table().get_pile_index_of_card(gainable_cards[index].get_name())
print("Player " + str(self.get_owner().get_player_index()) + " drawing "
+ self.get_owner().get_table().get_pile(pile_index).get_card_group().get_name() + " to hand.")
self.get_owner().get_table().get_pile(pile_index).transfer_top_card(self.get_owner().get_hand())
self.get_owner().claim_top_card(self.get_owner().get_hand())
chances = 0
def __get_gain_card(self):
return self.__Card_owner.get_general_input("\nPlease identify the index of which card you would like to "
return self.get_owner().get_general_input("\nPlease identify the index of which card you would like to "
"obtain: ", int)
def __get_gainable_cards(self, spending_limit):
result = list()
for p in self._Card__owner.get_table().get_piles():
for p in self.get_owner().get_table().get_piles():
if p.get_card_group().get_cost() <= spending_limit:
if self.gainable_type_restriction is None:
result.append(p.get_card_group())
elif p.get_card_group().get_type() in self.gainable_type_restriction:
elif isinstance(p.get_card_group(), self.gainable_type_restriction):
result.append(p.get_card_group())
return result

View File

@ -1,5 +1,5 @@
from card.card_trash import CardTrash
from card.card_gain import CardGain
from card.special.card_trash import CardTrash
from card.special.card_gain import CardGain
class CardGainTrash(CardTrash, CardGain):

View File

@ -6,10 +6,10 @@ class CardTrash(Card):
def trash_card_get_cost(self):
tc = self.__get_trashable_cards()
self._Card__print_card_list(tc, " Trashable Cards: ")
self.print_card_list(tc, " Trashable Cards: ")
index = 0
bonus = 0
chances = self._Card__owner.get_std_chances()
chances = self.get_owner().get_std_chances()
while 0 < len(tc) and 0 <= index < len(tc) - 1 and chances > 0:
index = self.__get_card_to_trash()
@ -19,9 +19,9 @@ class CardTrash(Card):
index = 0
chances -= 1
else:
print("Player " + str(self._Card__owner.get_player_index()) + " trashing " + tc[index].get_name() + ".")
print("Player " + str(self.get_owner().get_player_index()) + " trashing " + tc[index].get_name() + ".")
bonus = tc[index].get_cost()
self._Card__owner.get_hand().transfer_card_by_card(tc[index], self._Card__owner.get_table().get_trash())
self.get_owner().get_hand().transfer_card_by_card(tc[index], self.get_owner().get_table().get_trash())
chances = 0
return bonus
@ -29,15 +29,15 @@ class CardTrash(Card):
self.trash_card_get_cost()
def __get_card_to_trash(self):
return self.__Card_owner.get_general_input("\nPlease identify the index of the desired card to trash: ", int)
return self.get_owner().get_general_input("\nPlease identify the index of the desired card to trash: ", int)
def __get_trashable_cards(self):
result = list()
for c in self._Card__owner.get_hand().get_supply():
for c in self.get_owner().get_hand().get_supply():
if c != self:
if self.trashable_type_restriction is None:
result.append(c)
elif c.get_type() in self.trashable_type_restriction:
elif isinstance(c, self.trashable_type_restriction):
result.append(c)
return result

View File

@ -1,8 +0,0 @@
from card.card_gain import CardGain
class Workshop(CardGain):
coin_gain = 4
def effect(self):
self.gain_card(self.coin_gain)

View File

@ -1,8 +0,0 @@
<code_scheme name="Default" version="173">
<codeStyleSettings language="Python">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>

View File

@ -1,78 +0,0 @@
<application>
<component name="LocalDatabaseDriverManager" version="163">
<driver id="exasol">
<artifact name="Exasol" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="h2.unified">
<artifact name="H2" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="azure.ms">
<artifact name="SQL Server" use="true" />
<option name="auto-sync" value="true" />
<option name="send-app-info" value="false" />
</driver>
<driver id="oracle">
<artifact name="Oracle" use="true" />
<option name="auto-sync" value="true" />
<option name="auto-commit" value="false" />
</driver>
<driver id="derby.embedded">
<artifact name="Apache Derby" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="sybase.jtds">
<artifact name="jTDS (SQL Server and Sybase)" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="sqlserver.jtds">
<artifact name="jTDS (SQL Server and Sybase)" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="hsqldb.local">
<artifact name="HSQLDB" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="derby.remote">
<artifact name="Apache Derby" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="sqlite.xerial">
<artifact name="Xerial SQLiteJDBC" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="sybase.ase">
<option name="auto-sync" value="true" />
</driver>
<driver id="postgresql">
<artifact name="PostgreSQL" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="redshift">
<artifact name="Redshift" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="db2">
<artifact name="DB2" use="true" />
<option name="auto-sync" value="false" />
</driver>
<driver id="sqlserver.ms">
<artifact name="SQL Server" use="true" />
<option name="auto-sync" value="true" />
<option name="send-app-info" value="false" />
</driver>
<driver id="db2.jtopen">
<artifact name="JTOpen (DB2 iSeries)" use="true" />
<option name="auto-sync" value="false" />
</driver>
<driver id="mysql">
<artifact name="MySQL Connector/J" use="true" />
<option name="auto-sync" value="true" />
</driver>
<driver id="hsqldb.remote">
<artifact name="HSQLDB" use="true" />
<option name="auto-sync" value="true" />
</driver>
</component>
</application>

View File

@ -1,26 +0,0 @@
<application>
<component name="DatabaseSettings" version="2">
<csv-formats>
<csv-format name="Tab-separated (TSV)" id="Tab-separated (TSV)_id">
<data>
<record-format prefix="" suffix="" nullText="" quotationPolicy="as needed" valueSeparator="&#9;" recordSeparator="&#10;">
<quotation>
<quotes left="&quot;" right="&quot;" leftEscaped="&quot;&quot;" rightEscaped="&quot;&quot;" />
<quotes left="'" right="'" leftEscaped="''" rightEscaped="''" />
</quotation>
</record-format>
</data>
</csv-format>
<csv-format name="Comma-separated (CSV)" id="Comma-separated (CSV)_id">
<data>
<record-format prefix="" suffix="" nullText="" quotationPolicy="as needed" valueSeparator="," recordSeparator="&#10;">
<quotation>
<quotes left="&quot;" right="&quot;" leftEscaped="&quot;&quot;" rightEscaped="&quot;&quot;" />
<quotes left="'" right="'" leftEscaped="''" rightEscaped="''" />
</quotation>
</record-format>
</data>
</csv-format>
</csv-formats>
</component>
</application>

View File

@ -1,13 +0,0 @@
<application>
<component name="XDebuggerSettings">
<data-views />
<general />
<debuggers>
<debugger id="javascript">
<configuration>
<custom-object-presentation />
</configuration>
</debugger>
</debuggers>
</component>
</application>

View File

@ -3,7 +3,8 @@
<option name="FONT_SIZE" value="18" />
<option name="FONT_SCALE" value="1.5" />
</component>
<component name="EditorSettings">
<option name="USE_SOFT_WRAPS" value="CONSOLE" />
<component name="TodoConfiguration">
<pattern pattern="\btodo\b.*" />
<pattern pattern="\bfixme\b.*" />
</component>
</application>

View File

@ -1,5 +0,0 @@
<application>
<component name="FileTypeManager" version="17">
<ignoreFiles list="*$py.class;*.hprof;*.pyc;*.pyo;*.rbc;*.yarb;*~;.DS_Store;.git;.hg;.svn;CVS;__pycache__;_svn;vssver.scc;vssver2.scc;" />
</component>
</application>

View File

@ -1,7 +0,0 @@
<application>
<component name="FindSettings">
<option name="customScope" value="All Places" />
<option name="defaultScopeName" value="All Places" />
<option name="SEARCH_SCOPE" value="All Places" />
</component>
</application>

76
game.py
View File

@ -1,14 +1,21 @@
from table.table import Table
from player.human import Human
from player.bots.pure_big_money import Pure_Big_Money
from card.card import Card
from card.militia import Militia
from card.moat import Moat
from card.cellar import Cellar
from card.merchant import Merchant
from card.mine import Mine
from card.remodel import Remodel
from card.workshop import Workshop
from card.basic.card_action import Action
from card.basic.card_curse import Curse
from card.basic.card_victory import Victory
from card.named.province import Province
from card.named.estate import Estate
from card.named.copper import Copper
from card.named.silver import Silver
from card.named.gold import Gold
from card.named.militia import Militia
from card.named.moat import Moat
from card.named.cellar import Cellar
from card.named.merchant import Merchant
from card.named.mine import Mine
from card.named.remodel import Remodel
from card.named.workshop import Workshop
def main():
@ -31,14 +38,15 @@ def setup_new_game(game_list, parameter, card_info):
index = 0
for p in parameter[2:]:
if p:
for i in range(card_info[index][9]):
card = card_info[index][8](card_info[index][0], card_info[index][1], card_info[index][2],
card_info[index][3], card_info[index][4], card_info[index][5],
card_info[index][6], card_info[index][7], None)
for i in range(card_info[index][7].pile_setup(humans + bots)):
card = card_info[index][7](card_info[index][0], card_info[index][1], card_info[index][2],
card_info[index][3], card_info[index][4], card_info[index][5],
card_info[index][6], None)
if i == 0:
t.create_pile(card)
else:
t.get_pile(t.get_pile_index_of_card(card_info[index][0])).add_card(card)
card_info[index][7].setup()
index += 1
for i in range(humans):
@ -58,29 +66,29 @@ def setup_new_game(game_list, parameter, card_info):
def get_game_parameters():
# humans, bots, card #1, card #2, ... etc
return [1, 1, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True]
return [2, 1, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True]
def get_card_info():
# 0 1 2 3 4 5 6 7 8 9
# [name, cost, cardtype, v, c, a, b, d, class, count] - values to pass to Card()
return [["Copper", 0, Card.CardType.Treasure, 0, 1, 0, 0, 0, Card, 60], # 1
["Silver", 3, Card.CardType.Treasure, 0, 2, 0, 0, 0, Card, 40], # 2
["Gold", 6, Card.CardType.Treasure, 0, 3, 0, 0, 0, Card, 30], # 3
["Estate", 2, Card.CardType.Victory, 1, 0, 0, 0, 0, Card, 40], # 4
["Dutchy", 5, Card.CardType.Victory, 3, 0, 0, 0, 0, Card, 12], # 5
["Province", 8, Card.CardType.Victory, 6, 0, 0, 0, 0, Card, 12], # 6
["Curse", 0, Card.CardType.Curse, -1, 0, 0, 0, 0, Card, 10], # 7
["Cellar", 2, Card.CardType.Action, 0, 0, 1, 0, 0, Cellar, 10], # 8
["Market", 5, Card.CardType.Action, 0, 1, 1, 1, 1, Card, 10], # 9
["Merchant", 3, Card.CardType.Action, 0, 0, 1, 0, 1, Merchant, 10], # 10
["Militia", 4, Card.CardType.Attack, 0, 2, 0, 0, 0, Militia, 10], # 11
["Mine", 5, Card.CardType.Action, 0, 0, 0, 0, 0, Mine, 10], # 12
["Moat", 2, Card.CardType.Reaction, 0, 0, 0, 0, 2, Moat, 10], # 13
["Remodel", 4, Card.CardType.Action, 0, 0, 0, 0, 0, Remodel, 10], # 14
["Smithy", 4, Card.CardType.Action, 0, 0, 0, 0, 3, Card, 10], # 15
["Village", 3, Card.CardType.Action, 0, 0, 2, 0, 1, Card, 10], # 16
["Workshop", 4, Card.CardType.Action, 0, 0, 0, 0, 0, Workshop, 10]] # 17
# 0 1 2 3 4 5 6 7
# [name, cost, v, c, a, b, d, class] - values to pass to Card()
return [["Copper", 0, 0, 1, 0, 0, 0, Copper], # 0
["Silver", 3, 0, 2, 0, 0, 0, Silver], # 1
["Gold", 6, 0, 3, 0, 0, 0, Gold], # 2
["Estate", 2, 1, 0, 0, 0, 0, Estate], # 3
["Dutchy", 5, 3, 0, 0, 0, 0, Victory], # 4
["Province", 8, 6, 0, 0, 0, 0, Province], # 5
["Curse", 0, -1, 0, 0, 0, 0, Curse], # 6
["Cellar", 2, 0, 0, 1, 0, 0, Cellar], # 7
["Market", 5, 0, 1, 1, 1, 1, Action], # 8
["Merchant", 3, 0, 0, 1, 0, 1, Merchant], # 9
["Militia", 4, 0, 2, 0, 0, 0, Militia], # 10
["Mine", 5, 0, 0, 0, 0, 0, Mine], # 11
["Moat", 2, 0, 0, 0, 0, 2, Moat], # 12
["Remodel", 4, 0, 0, 0, 0, 0, Remodel], # 13
["Smithy", 4, 0, 0, 0, 0, 3, Action], # 14
["Village", 3, 0, 0, 2, 0, 1, Action], # 15
["Workshop", 4, 0, 0, 0, 0, 0, Workshop]] # 16
# Big Money
# ["Adventurer",
# ["Bureaucrat",
@ -107,8 +115,8 @@ def get_card_info():
def get_starting_deck():
# return [["Copper", 7], ["Estate", 3]]
return [["Market", 2], ["Merchant", 2], ["Smithy", 2], ["Village", 2], ["Moat", 2]]
return [["Copper", 7], ["Estate", 3]]
# return [["Market", 2], ["Merchant", 2], ["Smithy", 2], ["Village", 2], ["Moat", 2]]
# return [["Militia", 4], ["Cellar", 3], ["Moat", 3]]
# return [["Silver", 7], ["Merchant", 3]]
# return [["Copper", 4], ["Mine", 2], ["Remodel", 2], ["Workshop", 2]]

View File

@ -1,3 +0,0 @@
<profile version="1.0">
<option name="myName" value="Default" />
</profile>

View File

@ -1,6 +1,6 @@
from player.player import Player
from card.card import Card
#name943meats23jet
from card.basic.card_treasure import Treasure
class Pure_Big_Money(Player):
def take_action(self):
@ -12,7 +12,7 @@ class Pure_Big_Money(Player):
hand = self.get_hand().get_supply()
for c in hand:
if c.get_type() == Card.CardType.Treasure:
if isinstance(c, Treasure):
choice = hand.index(c)
print(message + str(choice))
@ -20,7 +20,7 @@ class Pure_Big_Money(Player):
#This method will only be called when it is time to buy things, a very simple logic will decide its action.
def get_buy_input(self, message, target_type):
coin = self._Player__purchase_power
coin = self.get_coin()
choice = -1
if coin >= 8:
@ -39,7 +39,10 @@ class Pure_Big_Money(Player):
min_coin = self.get_hand().get_supply()[choice].get_purchase_power()
for c in self.get_hand().get_supply():
if c.get_purchase_power() < min_coin and c.get_type() != Card.CardType.Treasure:
# We want to do isinstance rather than not isinstance because we only want to evaluate this loop when we are
# evaluating an all treasure card hand as at that point the choice will be a treasure card, otherwise the
# choice will already be non-treasure and we don't need to check anything since this bot doesn't do action
if c.get_purchase_power() < min_coin and isinstance(c, Treasure):
min_coin = c.get_purchase_power()
choice = self.get_hand().get_supply().index(c)
@ -48,7 +51,7 @@ class Pure_Big_Money(Player):
def __get_first_non_Treasure(self):
for c in self.get_hand().get_supply():
if c.get_type() != Card.CardType.Treasure:
if not isinstance(c, Treasure):
return self.get_hand().get_supply().index(c)
return 0

View File

@ -6,3 +6,9 @@ class Discard(Supply):
while self.get_remaining() > 0:
self.transfer_top_card(deck)
deck.shuffle()
def print(self):
if len(self.get_supply()) > 0:
print("Discard shows " + str(self.get_top_card()) + " face up.")
else:
print("Discard is empty.")

View File

@ -2,43 +2,32 @@ from table.supply import Supply
class Hand(Supply):
def contains_one_of(self, acceptible_types):
def contains_one_of(self, acceptable_class):
result = False
unique_types = self.__get_unique_types()
unique_class_instances = self.__get_unique_class_instances()
for at in acceptible_types:
result |= at in unique_types
for uci in unique_class_instances:
result |= isinstance(uci, acceptable_class)
return result
def get_card_type_count(self, card_type):
def get_card_type_count(self, card_class):
result = 0
for c in self._Supply__card:
if c.get_type() == card_type:
for c in self.get_supply():
if isinstance(c, card_class):
result += 1
return result
def blocks_attack(self, what_attack):
yes_no = False
found_at = -1
def reaction_blocks_attack(self, what_attack):
attack_blocked = False
for c in self.get_supply():
attack_blocked |= c.react(what_attack)
return attack_blocked
for c in self._Supply__card:
if c.prevent_attack:
found_at = self._Supply__card.index(c)
def __get_unique_class_instances(self):
unique_class_instances = list()
if found_at >= 0:
owner = self._Supply__card[found_at].get_owner()
yes_no = "Y" == owner.get_general_input("Player " + str(owner.get_player_index()) + ", enter 'Y' if you'd "
"like to reveal "
+ self._Supply__card[found_at].get_name() + " to block the "
+ what_attack + " attack: ", str)
return yes_no
def __get_unique_types(self):
unique_type = list()
for c in self._Supply__card:
current_type = c.get_type()
if not current_type in unique_type:
unique_type.append(current_type)
return unique_type
for c in self.get_supply():
if c not in unique_class_instances:
unique_class_instances.append(c)
return unique_class_instances

View File

@ -5,5 +5,5 @@ class Human(Player):
def __str__(self):
return "Player " + str(self.get_player_index()) + " (human)"
def militia_input(self, message):
return self.get_general_input(message)
def militia_input(self, message, target_type):
return self.get_general_input(message, target_type)

View File

@ -2,7 +2,8 @@ from player.deck import Deck
from player.discard import Discard
from player.hand import Hand
from player.counter import Counter
from card.card import Card
from card.basic.card_treasure import Treasure
from card.basic.card_action import Action
from random import randint
@ -41,6 +42,9 @@ class Player:
def get_player_index(self):
return self.__table.get_players().index(self)
def get_coin(self):
return self.__purchase_power
def get_score(self):
score = 0
@ -99,10 +103,10 @@ class Player:
def discard_from_hand(self, n):
self.__hand.transfer_card_by_index(n, self.__discard)
def play_card(self, acceptable_card_type, chances, counter):
if chances > 0 and self.__hand.contains_one_of(acceptable_card_type):
def play_card(self, acceptable_card_class, chances, counter):
if chances > 0 and self.__hand.contains_one_of(acceptable_card_class):
hand_index = self.get_play_input("\nPlease identify a card from hand to play by providing its index: ", int)
self.__check_play_card(hand_index, counter, acceptable_card_type, chances)
self.__check_play_card(hand_index, counter, acceptable_card_class, chances)
elif chances <= 0:
print("You have used up all of your chances to enter a valid integer; forfeiting remaining plays.")
if counter is not None:
@ -113,23 +117,22 @@ class Player:
counter.int = 0
def take_action(self):
print("\nPlease play an Action, Attack, or Reaction card until you have no remaining actions.")
print("\nPlease play an Action card until you have no remaining actions.")
while self.__actions.int > 0:
self.play_card([Card.CardType.Action, Card.CardType.Attack, Card.CardType.Reaction],
self.__std_chances, self.__actions)
self.play_card(Action, self.__std_chances, self.__actions)
def take_buy(self):
if self.__hand.contains_one_of([Card.CardType.Treasure]):
if self.__hand.contains_one_of(Treasure):
print("\nPlease play all Treasure card that you want to play.")
play_another = Counter(self.__hand.get_card_type_count(Card.CardType.Treasure))
play_another = Counter(self.__hand.get_card_type_count(Treasure))
while play_another.int > 0:
self.play_card([Card.CardType.Treasure], self.__std_chances, play_another)
self.play_card(Treasure, self.__std_chances, play_another)
self.buy_card(self.__std_chances)
def buy_card(self, chances):
self.__table.print()
while self.__buys > 0 and not self.__table.are_there_any_empty_piles() and chances > 0:
while self.__buys > 0 and not self.__table.are_there_three_empty_piles() and chances > 0:
pile_index = self.get_buy_input("\nPlease choose a pile from the table that you'd like to purchase: ", int)
if pile_index < 0:
@ -169,15 +172,15 @@ class Player:
print("\nPlayer " + str(self.__table.get_players().index(self)) + " Hand:")
self.__hand.print()
def __check_play_card(self, hand_index, counter, acceptable_card_type, chances):
def __check_play_card(self, hand_index, counter, acceptable_card_class, chances):
if hand_index < 0:
print("You have elected to forfeit any remaining plays.")
if counter is not None:
counter.int = 0
elif hand_index >= self.__hand.get_remaining():
print("Acceptable inputs range from 0 to " + str(self.__hand.get_remaining() - 1) + ". 1 chance lost.")
self.play_card(acceptable_card_type, chances - 1, counter)
elif self.__hand.get_card(hand_index).get_type() in acceptable_card_type:
self.play_card(acceptable_card_class, chances - 1, counter)
elif isinstance(self.__hand.get_card(hand_index), acceptable_card_class):
print("Player " + str(self.get_player_index()) + " playing: " + self.__hand.get_card(hand_index).get_name())
play_card = self.__hand.get_card(hand_index)
play_card.play()
@ -187,7 +190,7 @@ class Player:
self.__print()
else:
print("Index in bounds but not an acceptable card type. Chance to get it right reduced.")
self.play_card(acceptable_card_type, chances - 1, counter)
self.play_card(acceptable_card_class, chances - 1, counter)
# The following two methods are identical under different names so they can be overridden by bot classes later
def get_play_input(self, message, target_type):
@ -232,7 +235,6 @@ class Player:
return True
def __print_discard(self):
print("\nPlayer " + str(self.__table.get_players().index(self)) + " Discard:")
self.__discard.print()
def __print_deck(self):
@ -244,8 +246,9 @@ class Player:
print("Actions: " + str(self.__actions.int))
print("Buys: " + str(self.__buys))
print("Coin: " + str(self.__purchase_power))
self.print_hand()
print("Deck Remaining: " + str(self.__deck.get_remaining()))
self.__print_discard()
self.print_hand()
print("")
def __turn_setup(self):

View File

@ -1,5 +1,6 @@
from table.supply import Supply
class Pile(Supply):
def __init__(self, card):
self.__card_group = card

View File

@ -43,7 +43,8 @@ class Supply:
return self.__card[n]
def get_top_card(self):
return self.__card[len(self.__card) - 1]
if len(self.__card) > 0:
return self.__card[len(self.__card) - 1]
def get_remaining(self):
return len(self.__card)

View File

@ -1,5 +1,6 @@
from table.trash import Trash
from table.pile import Pile
from card.named.province import Province
class Table:
@ -45,22 +46,34 @@ class Table:
result = self.__pile.index(p)
return result
def are_there_any_empty_piles(self):
result = False
def are_there_three_empty_piles(self):
count = 0
for p in self.__pile:
result = result or p.get_remaining() == 0
return result
if p.get_remaining() == 0:
count += 1
return count > 2
def has_provinces_run_out(self):
for p in self.__pile:
if isinstance(p.get_card_group(), Province):
return p.get_remaining() == 0
return False
def should_game_end(self):
return self.are_there_three_empty_piles() or self. has_provinces_run_out()
def play(self):
turn = 0
# turn < 10 is for testing, otherwise endless as buying card is not yet done
while not self.are_there_any_empty_piles(): # and turn < 10:
player_turn = 0
should_continue = True
while should_continue:
# game ends after
should_continue = not self.should_game_end() or player_turn % len(self.__player) != 0
self.print()
self.__player[turn % len(self.__player)].take_turn()
turn += 1
self.__player[player_turn % len(self.__player)].take_turn()
player_turn += 1
else:
self.print()
print("\n\nGame had " + str(turn) + " turns in " + str(turn/len(self.__player)) + " rounds.")
print("\n\nGame had " + str(player_turn) + " turns in " + str(player_turn/len(self.__player)) + " rounds.")
for p in self.__player:
print("" + str(p) + " scored " + str(p.get_score()) + " points.")
if p.get_score() > self.__winning_score:

View File

@ -1,5 +0,0 @@
<application>
<component name="UISettings">
<option name="HIDE_TOOL_STRIPES" value="false" />
</component>
</application>