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.

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 withxpos, yposandyposhas negative values