0

I wrote this little chase game using the Blessed module. While writing, I tested it with URXVT. It works exactly how I want in that terminal emulator. However, when I try to run the game in Powershell on Windows, the raw Linux TTY, or even other Linux terminal emulators like Xterm or Alacritty, the tiles/squares don't form a grid how they should, and instead pile up at the left side of the screen. The terminal emulators which incorrectly display the grid provide 'True' for blessed.term.does_styling and .is_a_tty, and the opening and closing text prompts are printed to the screen in their proper places. Code below:

import sys, curses, blessed, time, random
from random import randrange
from blessed import *

term = blessed.Terminal()

# --- MISCELLANEOUS VARIABLES     ---

direction = ''

gameon = True

tilelist = []

#name = ''

# --- CLASSES                     ---

class Tile(object):
    '''A map tile.'''

    def __init__(self, name, number, xpos, ypos):
        '''Constructor
        pre: x/y position on terminal, whether a unit occupies the space, and the space's status e.g. wall, explosion, empty, etc
        post: tile object created with above variables defined'''

        self._name = name
        self._number = number
        self._xpos = xpos
        self._ypos = ypos

    def __str__(self):

        return self._name


class Player(object):
    '''player'''
    def __init__(self, name, ontile, hp, color, gold):
        self._name = name
        self._ontile = ontile
        self._hp = hp
        self._color = color
        self._gold = gold

    def __str__(self):
        return self._name

    def setupPlayer(self):
        playerinitpos = randrange(0, 4)
        if playerinitpos == 0:
            self._ontile = 1
        if playerinitpos == 1:
            self._ontile = 7
        if playerinitpos == 2:
            self._ontile = 43
        if playerinitpos == 3:
            self._ontile = 49
        self._hp = randrange(900, 1200)

    def movePlayer(self, direction):
        if self._ontile > 49:
            raise ValueError
        
        if direction == 'quit':
            self._hp = 0

        if direction == 'u':
            if self._ontile < 8:
                return 
            else:
                self._ontile = self._ontile - 7
                return self._ontile

        if direction == 'r':
            if self._ontile >= 49:
                return
            if self._ontile % 7 == 0:
                return
            else:
                self._ontile = self._ontile + 1
                return self._ontile

        if direction == 'd':
            if self._ontile > 42:
                return
            else:
                self._ontile = self._ontile + 7
                return self._ontile

        if direction == 'l':
            if self._ontile <= 1:
                return
            elif (self._ontile -1) % 7 == 0:
                return
            else:
                self._ontile = self._ontile - 1
                return self._ontile

        if direction == 'none':
            return self._ontile

    def drawPlayer(self):
        for i in tilelist:
            if i._number == self._ontile:
                drawTileColor(i, self._color)
                print(term.move_xy(i._xpos + 1, i._ypos - 1) + term.bold(self._name))

    def enemyGhostSeek(self, other):
        
        ghostlist = []
        smart = 1 + (getCounter() // 2)
        for i in range(smart):
            name = str('ghost' + str(i))
            ghostlist.append(Player(name, self._ontile, 0, [], 0))
        for i in ghostlist:

            count = 0
            color = []
            for c in range(20):
                while i._ontile != other._ontile:           
                    direc = randrange(0, 4)

                    if direc == 3:
                        direction = 'u'
                    if direc == 2:
                        direction = 'l'
                    if direc == 1:
                        direction = 'd'
                    if direc == 0:
                        direction = 'r'

                    i.movePlayer(direction)
                    i._color.append(direction)
                    count += 1
                    # for t in tilelist:
                        # if self._ontile == t._number:
                           # drawTileColor(t, term.red)
                           # print(term.move_xy(t._xpos + 2, t._ypos - 1) + term.bold + term.red('!'))
            i._hp = count
        cand = 100 
        for i in ghostlist:
            if i._hp < cand:
                cand = i._hp
        for i in ghostlist:
            if i._hp == cand and cand > 0:
                i._color.sort
                direction = i._color[0]
                return direction
            elif cand == 0:
                direction = 'none'
                return direction

# enemy deals damage

            if cand == 2:
                other._hp -= 40
            if cand == 1:
                other._hp -= 700

# gold stuff

    def drawGold(self):
        for i in tilelist:
            if self._ontile == i._number:
                print(term.move_xy(i._xpos + 1, i._ypos - 1) + term.goldenrod(' ● '))

    def moveGold(self):
        self._ontile = randrange(1, 49)

    def goldFoundCheck(self, other):
        if self._ontile == other._ontile:
            other._gold += randrange(1, 10)
            self.moveGold()


    def gethp(self):
        scale = '     '
        if self._hp >= 1100 and self._hp < 1300:
            scale = '██████'
        elif self._hp >= 1000 and self._hp < 1100:
            scale = '█████▌'
        elif self._hp >= 900 and self._hp < 1000:
            scale = '█████ '
        elif self._hp >= 800 and self._hp < 900:
            scale = '████▌ '
        elif self._hp >= 700 and self._hp < 800:
            scale = '████  '
        elif self._hp >= 600 and self._hp < 700:
            scale = '███▌  '
        elif self._hp >= 500 and self._hp < 600:
            scale = '███   '
        elif self._hp >= 400 and self._hp < 500:
            scale = '██▌   '
        elif self._hp >= 300 and self._hp < 400:
            scale = '██    '
        elif self._hp >= 200 and self._hp < 300:
            scale = '█▌    '
        elif self._hp >= 100 and self._hp < 200:
            scale = '█     '
        elif self._hp >= 50 and self._hp < 100:
            scale = '▌     '
        elif self._hp > 49:
            scale = '      '
        return scale

# --- FUNCTIONS                   ---


# player stuff

def getPlayerName():
    eraseAllTiles()
    term.clear()
    enterstr = 'please enter your name'
    d = 4
    for i in range(22):
        print(term.move_xy(d, 2) + term.bold(enterstr[i]))
        d += 1
        wait = .001 * randrange(1, 30)
        time.sleep(wait)
    print(term.move_xy(26, 2) + term.blink(':'))

# fun formatting / ani

def deleteLine(l, x, y):
    c = 0
    for i in range(l + 1):
        print(term.move_xy((x + l) - c, y) + term.bold(' '))
        c += 1
        wait = .001 * randrange(1, 10)
        time.sleep(wait)

def deleteMultiLine(l, d, x, y):
    c = 0
    dc = 0
    for i in range((l + 1) * d + 1):
        print(term.move_xy((x + l) - c, y + dc) + term.bold(' '))
        dc += 1
        if dc == d:
            dc = 0
            c+= 1
        wait = .001 * randrange(1, 15)
        time.sleep(wait)

def deleteFastMultiLine(l, d, x, y):
    c = 0
    dc = 0
    for i in range((l + 1) * d + 1):
        print(term.move_xy((x + l) - c, y + dc) + term.bold(' '))
        dc += 1
        if dc == d:
            dc = 0
            c+= 1
        wait = .0005  
        time.sleep(wait)

# tile stuff

def makeTiles():
    xplace = 70
    yplace = 0
    for i in range(1, 50):
        if (i - 1) % 7 == 0:
            yplace -= 3
            xplace -= 42
        tilename = ('tile' + str(i))
        tilelist.append(Tile(tilename, i, xplace, yplace))
        xplace += 6

def drawTile(i):
    print(term.move_xy(i._xpos, i._ypos) + term.bold('╭───╮'))
    print(term.move_xy(i._xpos, (i._ypos - 1)) + term.bold('│   │'))
    print(term.move_xy(i._xpos, (i._ypos - 2)) + term.bold('╰───╯'))

def drawTileColor(i, color):
    print(term.move_xy(i._xpos, i._ypos) + color + ('╭───╮'))
    print(term.move_xy(i._xpos, (i._ypos - 1)) + color + ('│   │'))
    print(term.move_xy(i._xpos, (i._ypos - 2)) + color + ('╰───╯'))


def drawAllTiles():
    for i in tilelist:
        drawTile(i)
        time.sleep(0.005)

def drawFastAllTiles():
    for i in tilelist:
        drawTile(i)

def eraseTile(i):
    print(term.move_xy(i._xpos, i._ypos) + term.bold('     '))
    print(term.move_xy(i._xpos, (i._ypos - 1)) + term.bold('     '))
    print(term.move_xy(i._xpos, (i._ypos - 2)) + term.bold('     '))

def eraseAllTiles():
    for i in tilelist:
        eraseTile(i)
        time.sleep(0.005)


# input

def getNameInput():
    p = 0
    name = ''
    while True:
        print(term.move_xy(28, 2) + term.blink('▂▂▂'))
        for i in range(4):
            if len(name) >= 3: 
                print(term.move_xy(26, 2) + term.bold(':'))
                print(term.move_xy(11, 3) + term.blink('press any key'))
            inp = term.inkey()
            if inp == '\n':
                break
            p += 1
            if len(name) <= 3:
                name = name + inp
            print(term.move_xy(28, 2) + term.bold(name))
        print(term.move_xy(28, 2) + term.bold('    '))
        name = name[0:3]
        if len(name) == 3 and type(name) == str:
            return name.lower()
            break
def getInputWait():
    inp = ''
    while inp != 'q':
        inp = term.inkey()
        if inp == 'n':
            return inp
        if inp == 'y':
            return inp
        if inp == 'q':
            return 'n'
        if inp == 'Q':
            return 'n'
        if inp == '\x1b':
            return 'n'
def getMoveInput():
    inp = term.inkey()
    dire = ''
    if inp == '\x1b[B':
        dire = 'd'
        return dire
    if inp == '\x1b[A':
        dire = 'u'
        return dire
    if inp == '\x1b[C':
        dire = 'r'
        return dire
    if inp == '\x1b[D':
        dire = 'l'
        return dire
    if inp == 'q': 
        dire = 'quit'
        return dire
    if inp == '\x1b':
        dire = 'quit'
        return dire
    if inp == 'Q':
        dire = 'quit'
        return dire
    else:
        dire = 'none'
        return dire


# turn counter

def incCounter():
    global counter
    counter += 1

def getCounter():
    global counter
    return counter


# game setup

def main():

    #quitgame = False
    gameon = True
    global counter
    counter = 0
    with term.fullscreen(), term.cbreak(), term.hidden_cursor():

        term.clear()    
        getPlayerName()
        playername = getNameInput()


# game loop

        while True:
            print(term.clear())
            gameon = True
            makeTiles()
            player1 = Player(playername, 0, 0, 0, 0)
            player1.setupPlayer()
            player1._color = term.purple
            print(term.clear)
            drawFastAllTiles()
            player1.drawPlayer()
            enemy = Player(' ! ', 0, 0, 0, 0)
            enemy.setupPlayer()
            enemy._color = term.red
            while enemy._ontile == player1._ontile:
                enemy.setupPlayer()
            enemy.drawPlayer()
            deleteMultiLine(23, 2, 4, 2)
            counter = 0
            Gold = Player('Gold', randrange(1, 49), 0, 0, 0)
            Gold.drawGold()
            while True:
                incCounter()
                print(term.move_xy(1, 1) + term.bold('       ' + term.move_xy(1, 2), '          '))
                print(term.move_xy(1, 1) + 'hp:' + term.purple(player1.gethp()) + term.move_xy(1, 2) + term.normal + 'gold:' + term.goldenrod(str(player1._gold)))
                Gold.goldFoundCheck(player1)
                if gameon == False:
                    break
                player1.movePlayer(getMoveInput())
                Gold.goldFoundCheck(player1)
                Gold.drawGold()
                drawFastAllTiles()
                player1.drawPlayer()
                enemy.movePlayer(enemy.enemyGhostSeek(player1))
                drawFastAllTiles()
                Gold.drawGold()
                enemy.drawPlayer()
                player1.drawPlayer()
                if player1._hp <= 0:
                    print(term.move_xy(1, 1) + term.bold('           ' + term.move_xy(1,2), '          '))
                    deleteFastMultiLine(55, 26, 20, 0)
                    gameon = False
     # play again?
            print(term.clear(), term.move_xy(2, 3),  term.bold(str(player1._name),', you lost after ', str(counter), ' moves. you had ', str(player1._gold), ' gold.'), term.move_down(5), term.move_left(41), term.blink('      play again? (y/n)'))
            yorno = getInputWait()
            if yorno == 'n':
                break
    sys.exit()

if __name__ == '__main__':
    main()

Program displaying correctly in URXVT Program displaying incorrectly in Alacritty

I'm stumped. Any assistance you can provide I appreciate!

Thank you

I wrote a text-based game and expected it to run similarly in different terminals, but it's only running correctly in the one I used while testing/writing the game.

3
  • when you format code then ``` has to be in separated line to correctly highlight code Commented Mar 5 at 11:23
  • you could add small image to show how it should look. Commented Mar 5 at 11:33
  • I would suggest to use print() to see if you correct values in variables but it can make problem with TUI - so maybe write some information to file/log to see if it has correctly values. I tried to do it with xpos, ypos and ypos has negative values Commented Mar 5 at 11:36

1 Answer 1

0

In Tile I wrote xpos, ypos in file to see values (because print() was useless with TUI)

class Tile(object):
    def __init__(self, name, number, xpos, ypos):
     
        with open('log.txt', 'a') as f:
            f.write(f'{xpos:2}, {ypos:2}\n') 

and it shows me negative values

28, -3
34, -3
40, -3

When I use self._ypos = -ypos then game looks good and I can play.

Problem solves also yplace += 3 instead of yplace -= 3 in makeTiles()

enter image description here

Tested on Linux Mint 22 on mate-terminal with tmux (and without tmux), xterm, Alacritty.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! This was very helpful and I was able to hack together a game that is working in different terminals, although I had to remove some animations that I couldn't get to work right. I'm still not sure why URXVT displayed the program differently than the other terminals.
I have no idea why URXVT displayed it. I don't have this terminal. I rather expected that you could accidently change one char (ie. - into + in yplace -= 3)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.