Giter VIP home page Giter VIP logo

taipan's Introduction

Taipan

Python implementation of the classic Apple II game, "Taipan!"

Based on Jay Link's C port which can be found here as well as in this repository.

taipan's People

Contributors

nelsonlove avatar

Watchers

 avatar

taipan's Issues

max_purchase is supposed to show up in a little box

max_purchase is supposed to show up in a little box

# TODO max_purchase is supposed to show up in a little box

    while True:
        max_purchase = int(player.cash / player.port[good])

        #  TODO max_purchase is supposed to show up in a little box
        amount = player.ui.ask_num(f"{string.goods_str(player.port)}\nYou can afford {max_purchase}. How much "
                                   f"{good} shall I buy, Taipan?")

29b05d468a2691e89fd026d37ccccccded1fbe0d

this should take a 'max' argument and return it if 'A' is entered

this should take a 'max' argument and return it if 'A' is entered

# TODO this should take a 'max' argument and return it if 'A' is entered

        elif wait:
            self.wait(wait)

    def ask(self, prompt='', validate=None, **kwargs):  # TODO is anything using kwargs here?
        self.tell(prompt + ' ')

        while True:
            result = get_str(**kwargs)
            if not validate or validate(result):  # TODO is anything using this?
                return result

    #  TODO this should take a 'max' argument and return it if 'A' is entered
    def ask_num(self, prompt, max_length=8):
        num_str = self.ask(prompt,
                           allowed=lambda c: str(c).isdigit(),
                           max_length=max_length)
        return int(num_str)

    def ask_orders(self, prompt, orders_list, other=None):

685c61d8dde962edf2f15d40367ff93203471908

refactoring this statement can wait until the whole method is refactored

refactoring this statement can wait until the whole method is refactored

rating_str = taipan.ui.abstract.term.reverse + rating_str + taipan.ui.abstract.term.normal

# TODO refactoring this statement can wait until the whole method is refactored

        for minimum, title, desc in ratings:
            rating_str = '{r:<13}   {d:>15}'.format(r=title, d=desc)
            if score <= minimum and not rating:
                # TODO refactoring this statement can wait until the whole method is refactored
                # rating_str = taipan.ui.abstract.term.reverse + rating_str + taipan.ui.abstract.term.normal
                rating = title
            self.ui.tell(rating_str)

d22c5f70bd21d115f235d7a40118e022ebca6fb2

"Comprador's Report" / "Captain's Report" should prefix some messages

"Comprador's Report" / "Captain's Report" should prefix some messages

# TODO "Comprador's Report" / "Captain's Report" should prefix some messages

        super().__init__(game)
        self.messages = None

    # TODO "Comprador's Report" / "Captain's Report" should prefix some messages
    def tell(self, message, **kwargs):
        self.messages.tell(message, **kwargs)

67564b9af3b53756837a8932f9c5dd583e0a9dea

make sure this displays as expected

make sure this displays as expected

# TODO make sure this displays as expected

            self.ui.tell(f"{ran} ran away, Taipan!")
            self.ui.update(self)

    # TODO make sure this displays as expected
    def throw_cargo(self):
        self.throw_cargo()
        prompt = string.goods_str(self.ship,
                                  "You have the following on board, Taipan:"
                                  ) + "What shall I throw overboard, Taipan?"

37644aaa1f8816930f29ceb81f569e1cc7a4bce7

all() isn't early exit, so all yes/no prompts are seen at every port

I changed them to use all(...) and self.ui.yes_no(...) for much more appealing game play. Like:

            self.player.cash >= self.cost,
            self.player.ship.free >= 10,
            self.player.ship.guns < 1000
        ]) and self.ui.yes_or_no(f"Do you wish to buy a ship's gun for {self.cost}, Taipan?")

Nice work, thanks!

'damaged' should be set in term.reverse if part of message

'damaged' should be set in term.reverse if part of message

# TODO 'damaged' should be set in term.reverse if part of message

        capacity_multiplier = int(game.player.ship.capacity / 50)
        self.cost = random.randint(0, time_multiplier) * capacity_multiplier + 1000

    def condition(self):
        return all([
            self.player.cash >= self.cost,
            self.ui.yes_or_no(f"Do you wish to trade in your {'damaged' if self.player.ship.damage else 'fine'} "
                              f"ship for one with 50 more capacity by paying an additional {self.cost}, Taipan?")
        ])

    def do(self):
        # TODO 'damaged' should be set in term.reverse if part of message
        self.player.cash -= self.cost
        self.player.ship.capacity += 50
        self.player.ship.damage = 0
        return True


class OpiumSeizure(Event):
    base_rate = 0.05

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fine = 0 if not self.player.cash else int((self.player.cash / 1.8) * random.random() + 1)

    def condition(self):
        return self.player.port.value != 1 and self.player.ship[Goods.OPIUM] > 0

    def do(self):
        self.ui.tell("Bad Joss!! The local authorities have seized your Opium cargo"
                     + (f" and have also fined you {self.fine}" if self.fine else "")
                     + ", Taipan!", wait=5)

        self.player.ship[Goods.OPIUM] = 0
        self.player.cash -= self.fine
        return True


class WarehouseTheft(Event):
    base_rate = 0.02

    def condition(self):
        return self.player.warehouse.used > 0

    def do(self):
        self.ui.tell("Messenger reports large theft from warehouse, Taipan.", wait=5)

        for good in Goods:
            self.player.warehouse[good] = int((self.player.warehouse[good] / 1.8) * random.random())

        return True


class LiWaits(Event):
    base_rate = 0.05

    def do(self):
        if self.player.li_timer > 0:
            self.player.li_timer += 1
        if self.player.li_timer == 4:
            self.player.li_timer = 0
        return True


class LiMessenger(Event):
    base_rate = 0.25

    def condition(self):
        return self.player.port.value != 1

    def do(self):
        self.ui.tell("Li Yuen has sent a Lieutenant, Taipan.  He says his admiral wishes to see you in Hong Kong, "
                     "posthaste!", wait=True)
        return True


class GoodPrices(Event):
    base_rate = 0.1

    def do(self):
        good = random.choice(list(Goods))
        current_price = self.player.port[good]

        if random.randint(0, 2) == 0:
            self.player.port[good] = int(current_price / 5)
            price_str = "has dropped to {p}!!"
        else:
            self.player.port[good] = int(current_price * (random.randint(0, 5) + 5))
            price_str = "has risen to {p}!!"

        msg = str("Taipan!!  The price of {n} " + price_str).format(n=str(good), p=self.player.port[good])
        self.ui.tell(msg, wait=True)

        return True


class Mugging(Event):
    base_rate = 0.05

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.amount = int((self.player.cash / 1.4) * random.random())

    def condition(self):
        return self.player.cash > 25000

    def do(self):
        self.player.cash -= self.amount

        self.ui.update()
        self.ui.tell(f"Bad Joss!! You've been beaten up and robbed of {self.amount} in cash, Taipan!!", wait=5)

        return True


class Storm(Event):
    base_rate = 0.1

    def do(self):
        self.ui.tell("Storm, Taipan!!", wait=True)

        if random.randint(0, 29):
            self.ui.tell("I think we're going down!!", wait=True)

            if self.player.ship.damage / self.player.ship.capacity * 3 * random.random() >= 1:
                self.ui.tell("We're going down, Taipan!!", wait=5)
                return False

        self.ui.tell("We made it!!", wait=True)

        if random.randint(0, 3) == 0:
            new_port = random.choice(list(set(Ports) - {self.player.port}))
            self.ui.tell(f"We've been blown off course to {new_port}", wait=True)
            self.player.port = new_port

        return True


class HostileEncounter(Event):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ships = min(9999, (random.randint(0, self.player.ship.capacity / 10) + self.player.ship.guns) + 1)

    def condition(self):
        return random.random() <= 1 / self.player.bp

    def do(self):
        self.ui.tell(f'{self.ships} hostile ships approaching, Taipan!', wait=True)

        result = Battle(self.game, self.ships).do()

        if result is BattleResult.BATTLE_INTERRUPTED:
            self.ui.update()  # TODO 'Location' in UI should read 'At sea' here
            self.ui.tell("Li Yuen's fleet drove them off!", wait=True)

        if any([
            result is BattleResult.BATTLE_INTERRUPTED,
            result is BattleResult.BATTLE_NOT_FINISHED
            and random.randint(0, 8 * self.player.li_timer + 4) == 0
        ]):
            self.ui.tell("Li Yuen's pirates, Taipan!!", wait=True)

            if self.player.li_timer > 0:
                self.ui.tell("Good joss!! They let us be!!", wait=True)
                return True

            else:
                self.ships = random.randint(0, (self.player.ship.capacity / 5) + self.player.ship.guns) + 5
                self.ui.tell(f"{self.ships} ships of Li Yuen's pirate fleet, Taipan!!", wait=True)
                result = Battle(self.game, self.ships, li=True).do()

        if result is not BattleResult.BATTLE_NOT_FINISHED:
            self.ui.update()  # TODO 'Location' in UI should read 'At sea' here

            if result is BattleResult.BATTLE_WON:
                booty = int((self.player.months / 4 * 1000 * self.ships) + random.randint(0, 1000) + 250)
                self.ui.tell(f"We captured some booty. It's worth {booty}!", wait=3)
                self.player.cash += booty

            elif result is BattleResult.BATTLE_FLED:
                self.ui.tell(f"We made it!", wait=3)

            else:
                assert (result != BattleResult.BATTLE_INTERRUPTED)
                self.ui.tell("The buggers got us, Taipan!!! It's all over, now!!!", wait=5)
                return False

        return True

f2ff2c6423423e692e99707abaaa7a1095cb58fa

Build instructions in readme.md

I'd love to give this a try, but I'm new to python and aren't familiar with the build tool you used. Some real quick instructions on how to run this would be fantastic.

"Comprador's Report" / "Captain's Report" should prefix some messages

"Comprador's Report" / "Captain's Report" should prefix some messages

# TODO "Comprador's Report" / "Captain's Report" should prefix some messages

from textwrap import wrap
from time import sleep

import blessed

from .functions import get_char, get_str

term = blessed.Terminal()

ALLOW_SKIP_WAIT = False


class UIObject:
    def __init__(self, x1=None, y1=None, x2=None, y2=None,
                 *,
                 parent=None,
                 corner_tr='┐',
                 corner_br='┘',
                 corner_bl='└',
                 corner_tl='┌',
                 side_t='─',
                 side_r='│',
                 side_b='─',
                 side_l='│',
                 border=True,
                 ignore_space=False
                 ):
        self.x1 = x1 or 0
        self.x2 = x2 or term.width - 1
        self.y1 = y1 or 0
        self.y2 = y2 or term.height - 1
        self.corner_tr = corner_tr
        self.corner_br = corner_br
        self.corner_tl = corner_tl
        self.corner_bl = corner_bl
        self.side_t = side_t
        self.side_r = side_r
        self.side_b = side_b
        self.side_l = side_l

        self.border = border
        self.ignore_space = ignore_space

        self.parent = parent
        self.children = []

        self.clear()

    @staticmethod
    def sleep(s):
        with term.cbreak(), term.hidden_cursor():
            sleep(s)

    @property
    def top(self):
        return self.parent.top_element if self.parent else self

    @property
    def size(self):
        x = self.x2 - self.x1 + 1
        y = self.y2 - self.y1 + 1
        return x, y

    @property
    def origin(self):
        return self.x1, self.x2

    def absolute_xy(self, x, y):
        return x + self.x1, y + self.y1

    def add_child(self, *children):
        for child in children:
            child.parent = self
            self.children.append(child)
        if len(children) == 1:
            return children[0]
        return children

    def move(self, x1, y1):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x1 + self.size[0] - 1
        self.y2 = y1 + self.size[1] - 1

    def center(self, v=True, h=True):
        parent_size = self.parent.size if self.parent else (term.width, term.height)
        x1 = parent_size[0] // 2 - self.size[0] // 2 if v else self.x1
        y1 = parent_size[1] // 2 - self.size[1] // 2 if h else self.y1
        self.move(x1, y1)

    def print_border(self):
        if not self.border:
            return

        with term.hidden_cursor():
            # Draw corners
            print(term.move_xy(self.x1, self.y1) + self.corner_tl, end='')
            print(term.move_xy(self.x2, self.y1) + self.corner_tr, end='')
            print(term.move_xy(self.x2, self.y2) + self.corner_br, end='')
            print(term.move_xy(self.x1, self.y2) + self.corner_bl, end='')

            # Draw horizontal sides
            side_width = self.x2 - self.x1 - 1
            if self.side_t:
                print(term.move_xy(self.x1 + 1, self.y1) + self.side_t * side_width, end='')
            if self.side_b:
                print(term.move_xy(self.x1 + 1, self.y2) + self.side_b * side_width, end='')

            # Draw vertical sides
            side_height = self.y2 - self.y1 - 1
            for i in range(side_height):
                if self.side_l:
                    print(term.move_xy(self.x1, self.y1 + 1 + i) + self.side_l, end='')
                if self.side_r:
                    print(term.move_xy(self.x2, self.y1 + 1 + i) + self.side_r, end='')

    @staticmethod
    def flush():
        with term.hidden_cursor():
            print('', end='', flush=True)

    def print(self, x, y, string, flush=True):
        x += self.x1 + int(self.border)
        y += self.y1 + int(self.border)

        with term.hidden_cursor():
            print(term.move_xy(x, y), end='')

            for char in str(string):
                if char == ' ' and self.ignore_space:
                    char = term.move_right(1)
                print(char, end='')

        if flush:
            self.flush()

    def clear(self):
        cols = self.size[0] - 2 * int(self.border)
        rows = self.size[1] - 2 * int(self.border)
        lines = [' ' * cols] * rows

        for y, line in enumerate(lines):
            self.print(0, y, line)

    def _update(self, *args, **kwargs):
        pass

    def update_all(self, *args, **kwargs):
        self.top.update(*args, **kwargs)

    def update(self, *args, **kwargs):
        self.print_border()
        self._update(*args, **kwargs)
        self.flush()
        for child in self.children:
            child.update(*args, **kwargs)


class StaticUIObject(UIObject):
    def __init__(self, x1, y1, content, color=None, ignore_space=True, **kwargs):
        self.color = color or term.normal
        self.lines = content.strip('\n').split('\n')

        x2 = x1 + len(self.lines[0]) - 1
        y2 = y1 + len(self.lines) - 1

        super().__init__(x1, y1, x2, y2, border=False, ignore_space=ignore_space, **kwargs)

    def animate(self, *gfx, delay=0.1):
        for graphic in gfx:
            self.graphic = graphic
            self.sleep(delay)

    @property
    def graphic(self):
        return '\n'.join(self.lines)

    @graphic.setter
    def graphic(self, string):
        self.lines = string.split('\n')
        self.update()

    def _update(self, *args, **kwargs):
        with term.hidden_cursor():
            print(self.color, end='')
            for y, line in enumerate(self.lines):
                self.print(0, y, line, flush=True)  # TODO are the two hidden cursor calls needed?
            print(term.normal, end='', flush=True)


class InteractiveUI(UIObject):
    def __init__(self, game, *args, **kwargs):
        self.game = game
        UIObject.__init__(self, *args, **kwargs)

    @property
    def player(self):
        return self.game.player

    @staticmethod
    def wait(timeout=3):
        if timeout is True:
            timeout = 3

        with term.cbreak(), term.hidden_cursor():
            if ALLOW_SKIP_WAIT:
                term.inkey(timeout=timeout)
            else:
                sleep(timeout)

    def tell(self, message, wait=False, clear=True):
        if clear:
            self.clear()

        # Reflow text
        lines = [line for block in message.split('\n')
                 for line in wrap(block, width=self.size[0] - 4)]

        for y, line in enumerate(lines):
            self.print(0, y, line)

        if wait:
            self.wait(wait)

    def ask(self, prompt='', **kwargs):
        self.tell(prompt + ' ')
        return get_str(**kwargs)

    def ask_num(self, prompt, max_length=8, max_num=None):
        num_str = self.ask(prompt,
                           allowed=lambda c: str(c).isdigit() or max_num and c.lower() == 'a',
                           max_length=max_length)
        if max_num and num_str.lower() == 'a':
            return max_num
        return int(num_str)

    def ask_orders(self, prompt, orders_list, other=None):
        def comma_list(str_list, conjunction='or'):
            return ', '.join(str_list[:-1]) + f', {conjunction} ' + str_list[-1]

        if '{}' in prompt:
            prompt = prompt.format(comma_list([str(order) for order in orders_list]))

        allowed = [order.shortcut for order in orders_list]
        if other:
            allowed += other

        self.tell(prompt)
        choice = get_char(allowed=allowed)

        for order in orders_list:
            if order.shortcut == choice:
                return order
        return choice

    def yes_or_no(self, prompt=''):
        self.tell(prompt)
        choice = get_char(allowed=['Y', 'y', 'N', 'n'])
        return choice.lower() == 'y'


class GameUI(InteractiveUI):
    def __init__(self, game):
        super().__init__(game)
        self.messages = None

    # TODO "Comprador's Report" / "Captain's Report" should prefix some messages
    def tell(self, message, **kwargs):
        self.messages.tell(message, **kwargs)

8a62dc1ef3ad01b1355b1b5649468a8d641d5eb0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.