'''
Bologna & Milano text only turn based game originally written in BASIC in the 1980's
Author: Rogerio Luz - rogeriosantosluz@gmail.com
Based on Pascal version: http://joaomorais.com.br/pascal/push.php?download=52539484
Thanks to joaomorais.com.br

v0.0.1 Simple engine to run a Text Game with few simple questions to build a Game, Players number and Players
v0.1.0 Game starts, # of players, choose players, Turns are working, economic options (buy/sell grain and land) are OK. 

TODO: Text to English, initial variables for all levels, better random values for production, calculate population, market, black death, invasion,
monetary, bank, maps, investments and promotions, better text format (tables)

'''
import sys
import random

#Class that represents a player
class Player:

    gender = None
    name = None

    title = 0
    title_male = ['SENHOR ','BARAO ','CONDE ','MARQUES ','DUQUE ','GRAO-DUQUE ','PRINCIPE ','* REI * ']
    title_female = ['DAMA ','BARONESA ','CONDESSA ','MARQUESA ','DUQUESA ','GRAO-DUQUESA ','PRINCESA ','* RAINHA * ']

    city = None

    land_price = 0
    grain_price = 0    

    production = 0
    production_rate = 0
    rats = 0
    cash = 0
    demand = 0
    backup = 0
    land = 0

    gain = 0
   
    economic_option = 0
    economic_options = []

    grain_to_buy = 0
    grain_to_sell = 0
    land_to_buy = 0
    land_to_sell = 0

    grains_to_people = 0

    lands_lost = 0
    soldiers = 0
    soldiers_lost = 0
    
    black_death_nobles = 0
    black_death_bishops = 0
    black_death_sellers = 0
    black_death_settlers = 0
    nobles = 0
    bishops = 0
    sellers = 0
    settlers = 0
    population = 0
 
    markets_profit = 0
    mills_profit = 0
    military_expenses = 0

    customs_taxes = 0
    selling_taxes = 0
    taxes = 0
    justice = None

    customs_taxes_profit = 0
    selling_taxes_profit= 0
    taxes_profit = 0
    total_profit = 0

    bank_interest = 0

    monetary_option = 0

    investments_option = 0
    
    #def __init__(self):
        #print("Player Iniciado")

    def input_gender(self):
        self.gender = input("Please type your gender(M/F): ")
        if self.gender != "M" and self.gender != "F":
            self.input_gender()

    def input_age(self):
        try:
            self.age = int( input("Please type your age in years (1 to 110): ") )
            if (self.age < 1 or self.age > 110):
                self.input_age()
        except ValueError:
            print("Invalid age.")
            self.input_age()

    def input_name(self):
        self.name = input("What your name ? ")

    def show_title(self):
        if self.gender == "M":
            return self.title_male[self.title]
        else:
            return self.title_female[self.title]
        
    def show_player(self, reg=0):
        print(self.show_title()+self.name)

# Class that represents the Game
class Game:

    players_num = 0
    players = []
    cities = ["BOLOGNA","MILANO","VENEZA","TORINO"]
    game_level = ['1. APRENDIZ','2. AVENTUREIRO','3. MESTRE','4. GRANDE MESTRE']
    type_of_justice = ["1 - FAIR", "2 - MODERATE", "3 - AGRESSIVE", "4 - ABUSIVE"]
    level = 0

    season = None
    seasons = ['ESTIAGEM - AMEACA DE FOME !','TEMPO RUIM - COLHEITA POBRE','TEMPO NORMAL - COLHEITA RAZOAVEL','TEMPO BOM - COLHEITA BOA','TEMPO OTIMO - COLHEITA EXCELENTE']
    
    over = False
    
    def __init__(self):
        self.clrscr(15)
        print("")
        print("")        
        print("WELCOME TO BOLOGNA & MILANO")
        print("")
        print("")
        print("BOLOGNA & MILANO IS A FANTASTIC GAME")
        print("")
        print("")
        print("THAT WILL TEST YOUR ABILITY TO MANAGER A CITY, STATE OR A NATION")
        print("")
        print("")        

    def players_number(self):
        try:
            self.players_num = int( input("How much players will play? (up to 4): ") )
            if (self.players_num < 1 or self.players_num > 4):
                self.players_number()
        except ValueError:
            print("Invalid number of players.")
            self.players_number()

    #Prompt player to few questions        
    def choose_player(self, reg):
        #print("Player #"+str(reg+1))
        print("Welcome Governor of "+self.cities[reg])
        player = Player()
        player.city = self.cities[reg]
        player.input_name()
        player.input_gender()
        #player.input_age()
        #player.show_player(reg)

        # we need to initialize some variables accordinly to the level of the game
        if self.level == 1:
            player.population = 1000
            player.production = 0
            player.rats = 0
            player.cash = 10000
            player.demand = 0
            player.backup = 10000
            player.land = 10000
            player.gain = 0
            player.soldiers = 500
            player.nobles = 0
            player.bishops = 0
            player.sellers = 0
            player.settlers = 0
            player.bank_interest = 5
        
        self.players.insert(reg,player)

    def show_instructions(self):
        print('A PARTIR DE AGORA  VOCE SERA O GOVERNANTE DE UMA CIDADE-ESTADO DO SECULO  XV.')
        print('A CADA PERIODO DE BOM GOVERNO VOCE RECEBERA TITULOS CADA VEZ MAIORES.');
        print('A EXPECTATIVA DE VIDA NAQUELA EPOCA ERA MUITO CURTA, LOGO VOCE DISPORA DE');
        print('POUCO TEMPO PARA GOVERNAR.');
        print('QUEM PRIMEIRO CHEGAR A REI OU RAINHA SERA O VENCEDOR DO JOGO.');
        print('O TAMANHO DA TORRE NO CANTO SUPERIOR ESQUERDO INDICARA SE SUAS DEFESAS SAO');
        print('ADEQUADAS.');
        print('O TERMOMETRO INDICA SE SUAS TERRAS ESTAO EM FRANCA PRODUCAO, CASO CONTRARIO');
        print('VOCE PRECISARA DE MAIS SERVOS.');
        print('UMA BOA DISTRIBUICAO DE GRAOS A POPULACAO, AUMENTARA  A TAXA DE NATALIDADE E');
        print('INCENTIVARA A MIGRACAO DE NOVOS SERVOS.');
        print('TAXAS E IMPOSTOS ELEVADOS, AUMENTAM A ARRECADACAO MAS AFETAM A ECONOMIA DA');
        print('NACAO.');
        print('FACA UM GOVERNO DEMOCRATICO E LEMBRE-SE QUE, SEMPRE BOM COMPRAR NA BAIXA PARA');
        print('VENDER NA ALTA.');
        print('GOOD LUCK!');

    def choose_level(self):
        print("GAME LEVEL")
        for level in self.game_level:
            print(level)

        try:
            self.level = int( input("Please Choose Game Level (1 to 4): ") )
            if (self.level < 1 or self.level > 4):
                self.choose_level()
        except ValueError:
            print("Invalid level.")
            self.choose_level()

    # In agriculture, the harvest is the process of gathering mature crops from the fields. 
    def show_season(self):
        print("Season:"+ self.season)

    def rats(self, player):
        
        if self.level == 0:
            player.rats = int(random.uniform(0,10))
        elif self.level == 1:
            player.rats = int(random.uniform(0,20))
        elif self.level == 2:
            player.rats = int(random.uniform(0,30))
        elif self.level == 3:
            player.rats = int(random.uniform(0,40))
        elif self.level == 4:
            player.rats = int(random.uniform(0,50))

        player.backup = player.backup - ( ( player.backup * player.rats ) / 100 )

    def random_season(self):
        self.season = random.choice(self.seasons)  

    def production(self, player):
        if self.level == 0:
            player.production_rate = int(random.uniform(0,50))
        elif self.level == 1:
            player.production_rate = int(random.uniform(0,40))
        elif self.level == 2:
            player.production_rate = int(random.uniform(0,30))
        elif self.level == 3:
            player.production_rate = int(random.uniform(0,20))
        elif self.level == 4:
            player.production_rate = int(random.uniform(0,10))

        if self.season == self.seasons[0]:
            player.production_rate = player.production_rate - ( ( player.production_rate * 100 ) / 100 )
            player.land_price = int(random.uniform(1,100) * player.production_rate)
            player.grain_price = int(random.uniform(40,50) * player.production_rate)
        elif self.season == self.seasons[1]:
            player.production_rate = player.production_rate - ( ( player.production_rate * 80 ) / 100 )
            player.land_price = int(random.uniform(100,200) * player.production_rate)
            player.grain_price = int(random.uniform(30,40) * player.production_rate)
        elif self.season == self.seasons[2]:
            player.production_rate = player.production_rate - ( ( player.production_rate * 60 ) / 100 )
            player.land_price = int(random.uniform(200,300) * player.production_rate)
            player.grain_price = int(random.uniform(20,30) * player.production_rate)
        elif self.season == self.seasons[3]:
            player.production_rate = player.production_rate - ( ( player.production_rate * 40 ) / 100 )
            player.land_price = int(random.uniform(300,400) * player.production_rate)
            player.grain_price = int(random.uniform(10,20) * player.production_rate)
        elif self.season == self.seasons[4]:
            player.production_rate = player.production_rate - ( ( player.production_rate * 20 ) / 100 )
            player.land_price = int(random.uniform(400,500) * player.production_rate)
            player.grain_price = int(random.uniform(1,10) * player.production_rate)

        player.production = int( player.backup + ( ( player.backup * player.production_rate ) / 100 ) )

        player.backup = int ( player.backup + player.production )

        player.demand = int (player.population + ( ( player.population * player.production_rate ) / 100 ) )


    def economic_options(self, player):

        self.clrscr(20)
        print("OS RATOS COMERAM "+str(player.rats)+"% DA RESERVA  DE GRAOS DOS SILOS DE ARMAZENAGEM")
        print("")
        print(self.show_season())
        print("PRODUCAO DE GRAOS "+str(player.production)+ " SACAS")
        print("")
        print('RESERVA  DEMANDA  PRECO   PRECO        CAIXA')
        print('GRAOS    GRAOS    GRAOS   TERRA');
        print(str(player.backup)+"    "+str(player.demand)+"      "+str(player.grain_price)+"         "+str(player.land_price)+"         "+str(player.cash))
        print(' SACAS    SACAS   MIL/SC  HECT.         FLORINS')
        print("")
        print('       OPCOES ECONOMICAS')
        print('       =================')
        print('       1. COMPRAR GRAOS ')
        print('       2. VENDER  GRAOS ')
        print('       3. COMPRAR TERRAS')
        print('       4. VENDER  TERRAS')
        print('       0. CONTINUAR     ')
        print("")
        print("AREA DA NACAO ==> "+str(player.land)+" HECT.")

        self.input_economic_option(player)        

    def input_economic_option(self, player):
        try:
            player.economic_option = int( input("Option? (0 to 4): ") )
            if (player.economic_option < 0 or player.economic_option > 4):
                self.input_economic_option(player)
        except ValueError:
            print("Invalid seconomic_option.")
            self.input_economic_option(player)
            
        self.choose_economic_option(player.economic_option, player)

    def choose_economic_option(self, option, player):
        if option == 1:
            self.buy_grain(player)
            self.economic_options(player)
        elif option == 2:
            self.sell_grain(player)
            self.economic_options(player)
        elif option == 3:
            self.buy_land(player)
            self.economic_options(player)
        elif option == 4:
            self.sell_land(player)
            self.economic_options(player)
        elif option == 0:
            None


    def buy_grain(self, player):
        try:
            player.grain_to_buy = int( input("How much grain will you buy?: ") )
            if (player.grain_to_buy < 0):
                self.buy_grain(player)
            if player.grain_price * player.grain_to_buy <  player.cash:
                player.cash -= ( player.grain_price * player.grain_to_buy )
                player.backup += player.grain_to_buy
            else:
                can_buy = player.cash / player.grain_price
                print("You dont have Cash to buy so much grain, you can buy up to "+str(int(can_buy))+ " grains")
                self.buy_grain(player)
        except ValueError:
            print("Invalid number of grains.")
            self.buy_grain(player)

    def sell_grain(self, player):
        try:
            player.grain_to_sell = int( input("How much grain will you sell?: ") )
            if (player.grain_to_sell < 0):
                self.sell_grain(player)
            if player.backup < player.grain_to_sell:
                print("You cant sell more than "+str(player.backup)+ " grains")
                self.sell_grain(player)
            else:
                player.backup -= player.grain_to_sell
                player.cash += ( player.grain_to_sell * player.grain_price )
        except ValueError:
            print("Invalid number of grains.")
            self.sell_grain(player)

    def buy_land(self, player):
        try:
            player.land_to_buy = int( input("How much land will you buy?: ") )
            if player.land_to_buy < 0:
                self.buy_land(player)
            if player.cash < ( player.land_to_buy * player.land_price ):
                can_buy = int( player.cash /  player.land_price)
                print("Buddy, you can buy up to "+ str(can_buy) + "HECTS")
                self.buy_land(player)
            else:
                player.cash -= ( player.land_to_buy * player.land_price )
                player.land += player.land_to_buy
        except ValueError:
            print("Invalid number of HECTS.")
            self.buy_land(player)

    def sell_land(self, player):
        try:
            player.land_to_sell = int( input("How much land will you sell?: ") )
            if player.land_to_sell < 0:
                self.sell_land(player)
            #Cant sell more than 50% of the land at a minimum of 1000 HECTS
            if ( ( player.land_to_sell > ( player.land * 0.5 ) ) or ( player.land - player.land_to_sell < 1000 )) :
                 print("Please sell less land... remember you need land for agriculture... ")
                 self.sell_land(player)
            else:
                player.land -= player.land_to_sell
                player.cash += player.land_to_sell * player.land_price
        except ValueError:
            print("Invalid number of HECTS.")
            self.sell_land(player)

    def grains_to_people(self, player):
        try:
            player.grains_to_people = int( input("How much grains will you give to your people ?: ") )
            if (player.grains_to_people < 0):
                self.grains_to_people(player)
            twenty_percent = int( player.backup * ( 0.2 ) )
            if ( player.backup - player.grains_to_people) < twenty_percent:
                can_give = player.backup - twenty_percent
                print("You need to keep 20% ("+ str(twenty_percent) +" grains) for your stock. So you can give up to "+str(can_give)+ " of grains.")
                self.grains_to_people(player)
            else:
                player.backup = player.backup - player.grains_to_people
        except ValueError:
            print("Invalid number of grains.")
            self.grains_to_people(player)

    def count_births(self, player):
        print(" N villagers born this season")

    def count_deaths(self, player):
        print(" N villagers died this season")

    def count_immigrants(self, player):
        print(" N villagers moved to your country this season")

    def count_emigrants(self, player):
        print(" N villagers moved to neighbor countries this season")

    def count_settlers(self, player):
        print(" This season your country has a population of N settlers")

    def population(self, player):
        self.count_births(player)
        self.count_deaths(player)
        self.count_immigrants(player)
        self.count_emigrants(player)
        self.count_settlers(player)

    def invasion(self, player):
        print("Sir XXX invaded your land and attached "+str(player.lands_lost)+" HECTs")
        print("You lost "+str(player.soldiers_lost)+" soldiers in the battlefield. Now you have "+str(player.soldiers))

    def market(self, player):
        print("Your markets profit were "+str(player.markets_profit)+" Florins")
        print("Your mills  profit were "+str(player.mills_profit)+" Florins")
        print("Military expenses were "+str(player.military_expenses)+" N Florins")

    #The Black Death was one of the most devastating pandemics in human history, peaking in Europe between 1348 and 1350
    def black_death(self, player):
        print("")
        print(' **** NOTICIAS CATASTROFICAS ****')
        print("")
        print('A PESTE NEGRA VARREU SUA CIDADE VITIMANDO:')
        print(str(player.black_death_nobles) + '   NOBRES +')
        print(str(player.black_death_bishops) + '   BISPOS E PADRES +')
        print(str(player.black_death_sellers) + '   COMERCIANTES +')
        print(str(player.black_death_settlers) + '   SERVOS +')

    def bank(self, player):
        print("Banks charged you in "+ str(player.bank_interest) + "% INTEREST ON DEBT")

    def monetary(self, player):
        print('RENDA DO ESTADO: ' +str(player.gain)+ ' FLORINS')
        print('***************')
        print('TAXAS DA  TAXAS S/  IMPOSTOS  JUSTICA')
        print('ALFANDEGA  VENDAS   DIVERSOS  (FORMA)')
        print("")
        print('    '+str(player.customs_taxes)+'%     '+str(player.selling_taxes)+'%        '+str(player.taxes)+'%     '+str(player.justice))
        print("")
        print('    '+str(player.customs_taxes_profit)+'      '+str(player.selling_taxes_profit)+'         '+str(player.taxes_profit)+'      '+str(player.total_profit))
        print('FLORINS  FLORINS    FLORINS    FLORINS')
        print("")
        print('         OPCOES FINANCEIRAS')
        print('         ==================')
        print('         1. TAXAS ALFANDEGARIAS')
        print('         2. TAXAS S/VENDAS     ')
        print('         3. IMPOSTOS DIVERSOS  ')
        print('         4. TAXAS JUDICIARIAS  ')
        print('         0. CONTINUAR          ')
        print("")
        print('OPCAO: ')

    def input_monetary_option(self, player):
        try:
            player.monetary_option = int( input("Option? (0 to 4): ") )
            if (player.monetary_option < 0 or player.monetary_option > 4):
                self.input_monetary_option(player)
        except ValueError:
            print("Invalid  monetary option.")
            self.input_monetary_option(player)
            
        self.choose_monetary_option(player.economic_option, player)

    def choose_monetary_option(self, option, player):
        if option == 1:
            self.customs_taxes(player)
        elif option == 2:
            self.selling_taxes(player)
        elif option == 3:
            self.taxes(player)
        elif option == 4:
            self.justice(player)
        elif option == 0:
            None

    def customs_taxes(self, player):
        try:
            player.customs_taxes = int( input("What is the new customs taxes (0 To 100%) ?: ") )
            if (player.customs_taxes < 0 or player.customs_taxes > 100):
                self.customs_taxes(player)
        except ValueError:
            print("Invalid number of %.")
            self.customs_taxes(player)        
        
    def selling_taxes(self, player):
        try:
            player.selling_taxes = int( input("What is the new selling_taxes (0 To 50%) ?: ") )
            if (player.selling_taxes < 0 or player.selling_taxes > 50):
                self.selling_taxes(player)
        except ValueError:
            print("Invalid number of %.")
            self.selling_taxes(player)

    def taxes(self, player):
        try:
            player.taxes = int( input("What is the new taxes (0 To 25%) ?: ") )
            if (player.taxes < 0 or player.selling_taxes > 25):
                self.staxes(player)
        except ValueError:
            print("Invalid number of %.")
            self.taxes(player)

    def justice(self, player):
        for justice in type_of_justice:
            print(justice)
        try:
            player.justice = int( input("Choose the kind of Justice (1 to 4): ") )
            if (player.justice < 1 or player.justice > 4):
                self.justice(player)
        except ValueError:
            print("Invalid option.")
            self.justice(player)

    def investments(self, player):
        print('INVESTIMENTOS DO ESTADO:')
        print('***********************')
        print('1. MERCADOS   (1000 FLORINS,CADA)')
        print('2. MOINHOS    (2000 FLORINS,CADA)')
        print('3. PALACIOS   (PARTE - 3000 FLORINS)')
        print('4. CATEDRAIS  (PARTE - 5000 FLORINS)')
        print('5. GASTOS MILITARES (FORMACAO DE UM PELOTAO - 500 FLORINS)')
        print('6. COMPARAR')
        print('7. MAPA')
        print('0. CONTINUAR')
        print
        print('DISPONIBILIDADE CAIXA: '+str(player.cash)+' FLORINS')

    def input_investments_option(self, player):
        try:
            player.investments_option = int( input("Option? (0 to 7): ") )
            if (player.investments_option < 0 or player.investments_option > 7):
                self.input_investments_option(player)
        except ValueError:
            print("Invalid investments option.")
            self.input_investments_option(player)
            
        self.choose_investments_option(player.investments_option, player)        
        
    def choose_investments_option(self, option, player):
        if option == 1:
            self.markets_to_buy(player)
        elif option == 2:
            self.mills_to_buy(player)
        elif option == 3:
            self.palaces_to_buy(player)
        elif option == 4:
            self.cathedral_to_buy(player)
        elif option == 5:
            self.troops_to_buy(player)
        elif option == 6:
            self.compare(player)
        elif option == 7:
            self.map(player)
        elif option == 0:
            None

    def markets_to_buy(self, player):
        try:
            player.markets_to_buy = int( input("How much markets will you buy?: ") )
            if (player.markets_to_buy < 0 or player.markets_to_buy > 10000):
                self.markets_to_buy(player)
        except ValueError:
            print("Invalid number of markets.")
            self.markets_to_buy(player)        

    def mills_to_buy(self, player):
        try:
            player.mills_to_buy = int( input("How much mills will you buy?: ") )
            if (player.mills_to_buy < 0 or player.mills_to_buy > 10000):
                self.mills_to_buy(player)
        except ValueError:
            print("Invalid number of mills.")
            self.mills_to_buy(player)

    def palace_to_buy(self, player):
        try:
            player.palace_to_buy = int( input("How much palace will you buy?: ") )
            if (player.palace_to_buy < 0 or player.palace_to_buy > 10000):
                self.palace_to_buy(player)
        except ValueError:
            print("Invalid number of palace.")
            self.palace_to_buy(player)                

    def cathedral_to_buy(self, player):
        try:
            player.cathedral_to_buy = int( input("How much cathedral will you buy?: ") )
            if (player.cathedral_to_buy < 0 or player.cathedral_to_buy > 10000):
                self.cathedral_to_buy(player)
        except ValueError:
            print("Invalid number of cathedral.")
            self.cathedral_to_buy(player)

    def troops_to_buy(self, player):
        try:
            player.troops_to_buy = int( input("How much troops will you buy?: ") )
            if (player.troops_to_buy < 0 or player.troops_to_buy > 10000):
                self.troops_to_buy(player)
        except ValueError:
            print("Invalid number of troops.")
            self.troops_to_buy(player)

    def compare(self, player):
        print('NOB  SOL  CLE  COM    SERV    TERRA    CAIXA');
        print('============================================');
        print(str(player.nobles)+"  "+str(player.nobles)+"  "+str(player.soldiers)+"  "+str(player.bishops)+"  "+str(player.sellers)+"  "+str(player.settlers)+"  "+str(player.cash))

    def map(self, player):
        print("MAP...")

    def promotion(self, player):
        print("")
        print("*******  CONGRATULATIONS *******")
        self.clrscr(5)
        print("You are now"+ player.show_title() + " of "+ player.city)

    def press_any_key(self):
        try:
            pressed = input("Press any key to Continue or Q to quit the game... ")
            if (pressed == "Q"):
                self.over = True
        except ValueError:
            print("Invalid key")
            self.press_any_key()        
              
    def clrscr(self,lines):
        for i in range(1, lines):
            print("")             

    #TURNS
    def start_turn(self):
        for player in self.players:
            self.clrscr(25)
            player.show_player()

            #We need to calculate production values according to the season and difficulty level
            # % of losts with rats
            self.rats(player)
            self.random_season()
            self.production(player)
            
            self.economic_options(player)
            self.grains_to_people(player)
            self.population(player)
            self.press_any_key()
            self.invasion(player)
            self.press_any_key()
            self.market(player)
            self.press_any_key()
            self.black_death(player)
            self.press_any_key()
            self.monetary(player)
            self.input_monetary_option(player)
            self.bank(player)
            self.press_any_key()            
            self.investments(player)
            self.input_investments_option(player)
            self.bank(player)
            self.press_any_key()
            self.promotion(player)
            self.press_any_key()            
                        
#starts the game
def start_game():
    game = Game()
    game.clrscr(15)
    game.choose_level()
    #prompts for the number of Players
    game.players_number()
    #for each player prompt for some questions and create players in the game
    for reg in range (0,game.players_num):
        game.choose_player(reg)
    # NOW that we have a Game and some Players we need to do something with them
    while not game.over:
        #TURNS
        game.start_turn()
        #game.over = True
    

#main
def main(argv):
    start_game()

if __name__ == "__main__":
    main(sys.argv)    


