├── README.md ├── african_head_diffuse.tga ├── chess.py ├── chess_lesson1.py ├── face.obj ├── fire.py ├── images ├── chess.png ├── fire.gif ├── lesson4.png ├── lesson5.png └── sokoban.png ├── lesson3.py ├── lesson4.py ├── lesson5.py ├── sokoban.py └── sokoban_config.py /README.md: -------------------------------------------------------------------------------- 1 | # PythonLessons 2 | **lesson3.py**, **lesson4.py**, **lesson5.py** - low-level pixel-by-pixel rendering. It's shown how to draw a line, a triangle, paint over a polygon, impose a texture, etc. 3 | 4 | 5 | ![lesson4](https://github.com/ef-end-y/PythonLessons/blob/master/images/lesson4.png)![lesson5](https://github.com/ef-end-y/PythonLessons/blob/master/images/lesson5.png) 6 | 7 | --- 8 | 9 | **chess.py** - a chess bot plays with itself. There are only pawns, rooks and kings, but you can add other pieces. This is an example how the bot thinks ahead and chooses the best move 10 | 11 | ![chess playing](https://github.com/ef-end-y/PythonLessons/blob/master/images/chess.png) 12 | 13 | --- 14 | 15 | **sokoban.py** is a telegram bot that plays Sokoban with you (https://en.wikipedia.org/wiki/Sokoban). Do not program like this - it's a hack in a certain way. I made it for small size only. 16 | 17 | ☿ - a warehouse keeper
18 | ◯ - a box
19 | ◉ - a box on a storage location
20 | 21 | ![sokoban playing](https://github.com/ef-end-y/PythonLessons/blob/master/images/sokoban.png) 22 | 23 | --- 24 | 25 | **fire.py** run it in console and get fire 26 | 27 | ![fire](https://github.com/ef-end-y/PythonLessons/blob/master/images/fire.gif) 28 | -------------------------------------------------------------------------------- /african_head_diffuse.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-end-y/PythonLessons/5195fa7f11e559f19f72aca085306283a1bc410d/african_head_diffuse.tga -------------------------------------------------------------------------------- /chess.py: -------------------------------------------------------------------------------- 1 | #! -*- coding: utf-8 -*- 2 | from abc import abstractmethod, ABCMeta 3 | 4 | 5 | THINKING_DEPTH = 4 6 | 7 | 8 | class Color(object): 9 | BLACK = 1 10 | WHITE = 2 11 | EMPTY = 0 12 | 13 | @classmethod 14 | def invert(cls, color): 15 | if color == cls.EMPTY: 16 | return color 17 | return cls.BLACK if color == cls.WHITE else cls.WHITE 18 | 19 | 20 | class Chessboard(object): 21 | SPACE_COLOR_WHITE = 209 22 | SPACE_COLOR_BLACK = 94 23 | 24 | board = None 25 | 26 | def fill(self): 27 | board = self.board = [[EmptyCell() for x in range(8)] for y in range(8)] 28 | black = Color.BLACK 29 | white = Color.WHITE 30 | # simple start position on the board 31 | board[1][0] = ChessmanPawn(black) 32 | board[1][1] = ChessmanPawn(black) 33 | board[1][2] = ChessmanPawn(black) 34 | board[1][3] = ChessmanKing(black) 35 | board[3][1] = ChessmanPawn(white) 36 | board[3][3] = ChessmanKing(white) 37 | board[2][2] = ChessmanRook(white) 38 | 39 | def clone(self): 40 | cb = Chessboard() 41 | cb.board = [self.board[i][:] for i in range(8)] 42 | return cb 43 | 44 | def get_chessman(self, x, y): 45 | return self.board[y][x] 46 | 47 | def get_color(self, x, y): 48 | return self.get_chessman(x, y).color 49 | 50 | def get_chessman_moves(self, x, y): 51 | return self.get_chessman(x, y).get_moves(self, x, y) 52 | 53 | def move_chessman(self, xy_from, xy_to): 54 | captured = self.board[xy_to[1]][xy_to[0]] 55 | self.board[xy_to[1]][xy_to[0]] = self.board[xy_from[1]][xy_from[0]] 56 | self.board[xy_from[1]][xy_from[0]] = EmptyCell() 57 | return captured 58 | 59 | def is_empty(self, x, y): 60 | return self.get_chessman(y, x).CODE == 'empty' 61 | 62 | def rate(self, color): 63 | res = 0 64 | pawn_x_position = [] 65 | for y in range(8): 66 | for x in range(8): 67 | if self.get_color(x, y) != color: 68 | continue 69 | chessman = self.get_chessman(x, y) 70 | res += chessman.rate(self, x, y) 71 | if chessman.CODE == 'pawn': 72 | pawn_x_position.append(x) 73 | # double pawns reduce the rate 74 | p = pawn_x_position 75 | res += 2 * (len(set(p)) - len(p)) 76 | # alone pawn reduce the rate 77 | for i in range(1, 6): 78 | if i in p and (i-1) not in p and (i+1) not in p: 79 | res -= 2 80 | return res 81 | 82 | def __str__(self): 83 | res = " a b c d e f g h\n" 84 | for y in range(8): 85 | res += "\033[0m" + str(8 - y) 86 | for x in range(8): 87 | color = self.SPACE_COLOR_BLACK if (x + y) % 2 else self.SPACE_COLOR_WHITE 88 | res += '\033[48;5;%sm%s ' % (color, self.board[y][x]) 89 | res += "\n" 90 | res += "\033[0m" 91 | return res 92 | 93 | 94 | class EmptyCell(object): 95 | CODE = 'empty' 96 | color = Color.EMPTY 97 | 98 | def get_moves(self, board, x, y): 99 | raise Exception('Error!') 100 | 101 | def rate(self, board, x, y): 102 | raise Exception('Error!') 103 | 104 | def __str__(self): 105 | return ' ' 106 | 107 | 108 | class Chessman(object): 109 | __metaclass__ = ABCMeta 110 | 111 | CODE = None 112 | VALUE = None 113 | WHITE_IMG = None 114 | BLACK_IMG = None 115 | 116 | color = None 117 | 118 | def __init__(self, color): 119 | self.color = color 120 | 121 | @abstractmethod 122 | def get_moves(self, board, x, y): 123 | return [] 124 | 125 | @abstractmethod 126 | def rate(self, board, x, y): 127 | return 0 128 | 129 | def enemy_color(self): 130 | return Color.invert(self.color) 131 | 132 | def __str__(self): 133 | return self.WHITE_IMG if self.color == Color.WHITE else self.BLACK_IMG 134 | 135 | 136 | class ChessmanPawn(Chessman): 137 | CODE = 'pawn' 138 | VALUE = 10 139 | WHITE_IMG = '♙' 140 | BLACK_IMG = '♟' 141 | 142 | def get_moves(self, board, x, y): 143 | moves = [] 144 | y += -1 if self.color == Color.WHITE else 1 145 | if y == -1 or y == 8: 146 | return moves 147 | if x > 0 and board.get_color(x-1, y) == self.enemy_color(): 148 | moves.append([x-1, y]) 149 | if x < 7 and board.get_color(x+1, y) == self.enemy_color(): 150 | moves.append([x+1, y]) 151 | if board.is_empty(x, y): 152 | moves.append([x, y]) 153 | if self.color == Color.WHITE and y == 5 and board.is_empty(x, y-1): 154 | moves.append([x, y-1]) 155 | if self.color == Color.BLACK and y == 2 and board.is_empty(x, y+1): 156 | moves.append([x, y+1]) 157 | return moves 158 | 159 | def rate(self, board, x, y): 160 | return self.VALUE + 1 * (8-y if self.color == Color.WHITE else y) 161 | 162 | 163 | class ChessmanKing(Chessman): 164 | CODE = 'king' 165 | VALUE = 0 166 | WHITE_IMG = '♔' 167 | BLACK_IMG = '♚' 168 | 169 | def get_moves(self, board, x, y): 170 | moves = [] 171 | for j in (y-1, y, y+1): 172 | for i in (x-1, x, x+1): 173 | if i == x and j == y: 174 | continue 175 | if 0 <= i <= 7 and 0 <= j <= 7 and board.get_color(i, j) != self.color: 176 | moves.append([i, j]) 177 | return moves 178 | 179 | def rate(self, board, x, y): 180 | return self.VALUE 181 | 182 | 183 | class ChessmanRook(Chessman): 184 | CODE = 'rook' 185 | VALUE = 50 186 | WHITE_IMG = '♖' 187 | BLACK_IMG = '♜' 188 | 189 | def get_moves(self, board, x, y): 190 | moves = [] 191 | for j in (-1, 1): 192 | i = x + j 193 | while 0 <= i <= 7: 194 | color = board.get_color(i, y) 195 | if color == self.color: 196 | break 197 | moves.append([i, y]) 198 | if color != Color.EMPTY: 199 | break 200 | i += j 201 | for j in (-1, 1): 202 | i = y + j 203 | while 0 <= i <= 7: 204 | color = board.get_color(x, i) 205 | if color == self.color: 206 | break 207 | moves.append([x, i]) 208 | if color != Color.EMPTY: 209 | break 210 | i += j 211 | return moves 212 | 213 | def rate(self, board, x, y): 214 | return self.VALUE 215 | 216 | 217 | class AI(object): 218 | def __init__(self, my_color, depth): 219 | self.my_color = my_color 220 | self.enemy_color = Color.invert(my_color) 221 | self.depth = depth 222 | 223 | def do(self, board, depth=0): 224 | enemy = bool(depth % 2) 225 | color = self.enemy_color if enemy else self.my_color 226 | if depth == self.depth: 227 | return board.rate(self.my_color) - board.rate(self.enemy_color)*1.1 228 | rates = [] 229 | for y in range(8): 230 | for x in range(8): 231 | if board.get_color(x, y) != color: 232 | continue 233 | xy_from = [x, y] 234 | for xy_to in board.get_chessman_moves(x, y): 235 | new_board = board.clone() 236 | target_cell = new_board.move_chessman(xy_from, xy_to) 237 | captured = target_cell.CODE != 'empty' 238 | if captured and target_cell.CODE == 'king': 239 | rate = -1000 if enemy else 1000 # king capturing 240 | else: 241 | rate = self.do(new_board, depth + 1) 242 | if rate is None: 243 | continue 244 | if captured and not enemy: 245 | rate += self.depth - depth # a little more aggression 246 | if depth: 247 | rates.append(rate) 248 | else: 249 | rates.append([rate, xy_from, xy_to]) 250 | if not depth: 251 | return rates 252 | if not rates: 253 | return None 254 | rate = min(rates) if enemy else max(rates) 255 | return rate 256 | 257 | 258 | class Game(object): 259 | @staticmethod 260 | def clear_screen(): 261 | print "\033[2J\033[1;3H\033[14;0m" 262 | 263 | def __init__(self): 264 | cb = Chessboard() 265 | cb.fill() 266 | 267 | self.clear_screen() 268 | print cb 269 | 270 | color = Color.WHITE 271 | for i in range(22): 272 | max_rate = -9999 273 | xy_from = xy_to = None 274 | rates = AI(color, THINKING_DEPTH).do(cb) 275 | for rate in rates: 276 | if rate[0] < max_rate: 277 | continue 278 | max_rate, xy_from, xy_to = rate 279 | if not xy_from: 280 | print 'end' 281 | exit() 282 | cb.move_chessman(xy_from, xy_to) 283 | color = Color.invert(color) 284 | self.clear_screen() 285 | print cb 286 | 287 | Game() 288 | -------------------------------------------------------------------------------- /chess_lesson1.py: -------------------------------------------------------------------------------- 1 | #! -*- coding: utf-8 -*- 2 | 3 | 4 | class Color(object): 5 | EMPTY = 0 6 | BLACK = 1 7 | WHITE = 2 8 | 9 | 10 | class Empty(object): 11 | color = Color.EMPTY 12 | 13 | def get_moves(self, board, x, y): 14 | raise Exception('Error !') 15 | 16 | def __str__(self): 17 | return '.' 18 | 19 | 20 | class ChessMan(object): 21 | IMG = None 22 | 23 | def __init__(self, color): 24 | self.color = color 25 | 26 | def __str__(self): 27 | return self.IMG[0 if self.color == Color.WHITE else 1] 28 | 29 | 30 | class Pawn(ChessMan): 31 | IMG = ('♙', '♟') 32 | 33 | def get_moves(self, board, x, y): 34 | moves = [] 35 | if self.color == Color.BLACK and y < 7 and board.get_color(x, y+1) == Color.EMPTY: 36 | moves.append([x, y+1]) 37 | return moves 38 | 39 | 40 | class King(ChessMan): 41 | IMG = ('♔', '♚') 42 | 43 | def get_moves(self, board, x, y): 44 | moves = [] 45 | return moves 46 | 47 | 48 | class Board(object): 49 | def __init__(self): 50 | self.board = [[Empty()] * 8 for y in range(8)] 51 | self.board[1][2] = Pawn(Color.BLACK) 52 | self.board[0][3] = King(Color.BLACK) 53 | self.board[7][3] = King(Color.WHITE) 54 | 55 | def get_color(self, x, y): 56 | return self.board[y][x].color 57 | 58 | def get_moves(self, x, y): 59 | return self.board[y][x].get_moves(self, x, y) 60 | 61 | def move(self, xy_from, xy_to): 62 | self.board[xy_to[1]][xy_to[0]] = self.board[xy_from[1]][xy_from[0]] 63 | self.board[xy_from[1]][xy_from[0]] = Empty() 64 | 65 | def __str__(self): 66 | res = '' 67 | for y in range(8): 68 | res += ''.join(map(str, self.board[y])) + "\n" 69 | return res 70 | 71 | 72 | b = Board() 73 | print b 74 | m = b.get_moves(2, 1) 75 | b.move([2,1], m[0]) 76 | print b 77 | -------------------------------------------------------------------------------- /fire.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | from time import sleep 3 | 4 | flame = ' .,:;=<*i%IHM#' 5 | 6 | fw = 128 # fire width 7 | fh = 60 # fire height 8 | 9 | fire = [] 10 | 11 | for y in range(fh): 12 | fire.append([0] * fw) 13 | 14 | a = b = 0 15 | f = len(flame) - 1 16 | 17 | for i in range(500): 18 | print("\033[2J\033[0;0H") 19 | 20 | for y in range(fh): 21 | print(''.join([flame[fire[fh - y - 1][x]] for x in range(fw)])) 22 | 23 | for x in range(fw): 24 | fade0 = int(0.15 * x * abs(x - fw)) + 1 25 | for y in range(1, fh): 26 | x1 = max(min(x + randint(0, 5) - 2, fw - 1), 0) 27 | fade = int(fw/randint(1, fade0)) 28 | fire[y][x] = max(fire[y - 1][x1] - fade, 0) 29 | 30 | sleep(0.07) 31 | 32 | for x in range(0, randint(0, fw)): 33 | fire[0][x] = f if (x > a < b) or (x < a > b) else 0 34 | a += 1 if a < b else -1 35 | if a == b: 36 | b = randint(0, int(fw/2)) 37 | 38 | 39 | -------------------------------------------------------------------------------- /images/chess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-end-y/PythonLessons/5195fa7f11e559f19f72aca085306283a1bc410d/images/chess.png -------------------------------------------------------------------------------- /images/fire.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-end-y/PythonLessons/5195fa7f11e559f19f72aca085306283a1bc410d/images/fire.gif -------------------------------------------------------------------------------- /images/lesson4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-end-y/PythonLessons/5195fa7f11e559f19f72aca085306283a1bc410d/images/lesson4.png -------------------------------------------------------------------------------- /images/lesson5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-end-y/PythonLessons/5195fa7f11e559f19f72aca085306283a1bc410d/images/lesson5.png -------------------------------------------------------------------------------- /images/sokoban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-end-y/PythonLessons/5195fa7f11e559f19f72aca085306283a1bc410d/images/sokoban.png -------------------------------------------------------------------------------- /lesson3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import random 3 | from PIL import Image 4 | import re 5 | 6 | scr_x = 800 # Ширина картинки 7 | scr_y = scr_x # Высота картинки 8 | img = Image.new('RGB', (scr_x, scr_y), 'black') # Создадим картинку с черным цветом фона 9 | canvas = img.load() # Через эту переменную у нас есть доступ к пикселям картинки 10 | 11 | 12 | class Point(object): 13 | def __init__(self, x, y): 14 | self.x = x 15 | self.y = y 16 | 17 | def show(self, color=None): 18 | canvas[self.x, scr_y-self.y] = color or (255, 255, 255) 19 | 20 | def copy(self): 21 | return Point(self.x, self.y) 22 | 23 | 24 | def zero_div(a, b): 25 | return float(a)/b if b else 0 26 | 27 | 28 | def triangle(coords, color): 29 | a, b, c = sorted(coords, key=lambda p: p.y) 30 | p1 = a.copy() 31 | p2 = a.copy() 32 | delta_p1 = zero_div((b.x - a.x), (b.y - a.y)) 33 | delta_p2 = zero_div((c.x - a.x), (c.y - a.y)) 34 | for y in (b.y, c.y): 35 | while p1.y < y: 36 | if p1.x > p2.x: 37 | p3 = p2.copy() 38 | x = p1.x 39 | else: 40 | p3 = p1.copy() 41 | x = p2.x 42 | while p3.x < x: 43 | p3.show(color) 44 | p3.x += 1 45 | p1.y += 1 46 | p2.y += 1 47 | p1.x += delta_p1 48 | p2.x += delta_p2 49 | delta_p1 = zero_div((c.x - b.x), (c.y - b.y)) 50 | 51 | half_scr_x = int(scr_x / 2) 52 | half_scr_y = int(scr_y / 2) 53 | f = open('face.obj', 'r') 54 | lines = f.read() 55 | points = [] 56 | for line in lines.split('\n'): 57 | try: 58 | v, x, y, z = re.split('\s+', line) 59 | except: 60 | continue 61 | if v == 'v': 62 | x = int((float(x) + 1) * half_scr_x) 63 | y = int((float(y) + 1) * half_scr_y) 64 | points.append(Point(x, y)) 65 | if v == 'f': 66 | color = tuple([random.randint(0, 255)] * 3) 67 | triangle([points[int(i.split('/')[0])-1] for i in (x, y, z)], color) 68 | 69 | img.show() 70 | 71 | -------------------------------------------------------------------------------- /lesson4.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PIL import Image 3 | import re 4 | 5 | scr_x = 800 # Ширина картинки 6 | scr_y = scr_x # Высота картинки 7 | 8 | 9 | class Screen(object): 10 | def __init__(self, width, height): 11 | self.width = width 12 | self.height = height 13 | self.img = Image.new('RGB', (width, height), 'black') 14 | self.canvas = self.img.load() 15 | self.z_buffer = [[0] * width for i in range(height)] 16 | 17 | def point(self, *coords): 18 | return Point(self, *coords) 19 | 20 | @staticmethod 21 | def triangle(coords): 22 | a, b, c = sorted(coords, key=lambda p: p.y) 23 | p1 = a.copy() 24 | p2 = a.copy() 25 | height_ac = c.y - a.y 26 | height_ab = (b.y - a.y) or 1 27 | height_bc = (c.y - b.y) or 1 28 | delta_x1 = float(b.x - a.x) / height_ab 29 | delta_x2 = float(c.x - a.x) / height_ac 30 | delta_z1 = float(b.z - a.z) / height_ab 31 | delta_z2 = float(c.z - a.z) / height_ac 32 | for y in (b.y, c.y): 33 | while p1.y < y: 34 | if p1.x > p2.x: 35 | p3 = p2.copy() 36 | p4 = p1 37 | else: 38 | p3 = p1.copy() 39 | p4 = p2 40 | delta_z3 = float(p4.z - p3.z) / ((p4.x - p3.x) or 1) 41 | while p3.x < p4.x: 42 | p3.show(tuple([int(p3.z * 128)] * 3)) 43 | p3.x += 1 44 | p3.z += delta_z3 45 | p1.y += 1 46 | p2.y += 1 47 | p1.x += delta_x1 48 | p1.z += delta_z1 49 | p2.x += delta_x2 50 | p2.z += delta_z2 51 | delta_x1 = float(c.x - b.x) / height_bc 52 | delta_z1 = float(c.z - b.z) / height_bc 53 | p1 = b.copy() 54 | 55 | 56 | class Point(object): 57 | def __init__(self, screen, x, y, z): 58 | self.x = x 59 | self.y = y 60 | self.z = z 61 | self.screen = screen 62 | 63 | def show(self, color=None): 64 | screen = self.screen 65 | x = int(self.x) 66 | y = int(self.y) 67 | if self.z <= screen.z_buffer[x][y]: 68 | return 69 | screen.z_buffer[x][y] = self.z 70 | screen.canvas[x, screen.height-y] = color or (255, 255, 255) 71 | 72 | def copy(self): 73 | return Point(self.screen, self.x, self.y, self.z) 74 | 75 | 76 | def show_face(): 77 | half_scr_x = int(scr_x/2) 78 | half_scr_y = int(scr_y/2) 79 | f = open('face.obj', 'r') 80 | lines = f.read() 81 | points = [] 82 | screen = Screen(scr_x, scr_y) 83 | for line in lines.split('\n'): 84 | try: 85 | v, x, y, z = re.split('\s+', line) 86 | except ValueError: 87 | continue 88 | if v == 'v': 89 | x = int((float(x) + 1) * half_scr_x) 90 | y = int((float(y) + 1) * half_scr_y) 91 | z = float(z) + 1 92 | points.append(screen.point(x, y, z)) 93 | if v == 'f': 94 | screen.triangle([points[int(i.split('/')[0])-1] for i in (x, y, z)]) 95 | screen.img.show() 96 | 97 | show_face() 98 | -------------------------------------------------------------------------------- /lesson5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import copy 3 | from PIL import Image 4 | import re 5 | 6 | scr_x = 800 # Ширина картинки 7 | scr_y = scr_x # Высота картинки 8 | 9 | 10 | class Screen(object): 11 | def __init__(self, width, height): 12 | self.width = width 13 | self.height = height 14 | self.img = Image.new('RGB', (width, height), 'black') 15 | self.canvas = self.img.load() 16 | self.z_buffer = [[0] * width for i in range(height)] 17 | 18 | def point(self, *coords): 19 | return TexturePoint(self, *coords) 20 | 21 | @staticmethod 22 | def triangle(coords, texture): 23 | a, b, c = sorted(coords, key=lambda p: p.y) 24 | p1, p2 = a.copy(), a.copy() 25 | height = c.y - a.y 26 | delta_x2 = float(c.x - a.x) / height 27 | deltas = lambda i, j, divider: [float(i.z-j.z)/divider, float(i.u-j.u)/divider, float(i.v-j.v)/divider] 28 | delta_z2, delta_u2, delta_v2 = deltas(c, a, height) 29 | for p in (b, c): 30 | height = (p.y - p1.y) or 1 31 | delta_x1 = float(p.x - p1.x) / height 32 | delta_z1, delta_u1, delta_v1 = deltas(p, p1, height) 33 | while p1.y < p.y: 34 | p3, p4 = (p2.copy(), p1) if p1.x > p2.x else (p1.copy(), p2) 35 | delta_z3, delta_u3, delta_v3 = deltas(p4, p3, (p4.x - p3.x) or 1) 36 | while p3.x < p4.x: 37 | p3.show(texture[p3.u, p3.v]) 38 | p3.add(x=1, z=delta_z3, u=delta_u3, v=delta_v3) 39 | p1.add(x=delta_x1, y=1, z=delta_z1, u=delta_u1, v=delta_v1) 40 | p2.add(x=delta_x2, y=1, z=delta_z2, u=delta_u2, v=delta_v2) 41 | p1 = b.copy() 42 | 43 | 44 | class Point(object): 45 | def __init__(self, screen, x, y, z): 46 | self.x = x 47 | self.y = y 48 | self.z = z 49 | self.screen = screen 50 | 51 | def show(self, color=None): 52 | screen = self.screen 53 | x = int(self.x) 54 | y = int(self.y) 55 | if self.z <= screen.z_buffer[y][x]: 56 | return 57 | screen.z_buffer[y][x] = self.z 58 | screen.canvas[x, screen.height-y] = color or (255, 255, 255) 59 | 60 | def copy(self): 61 | return copy.copy(self) 62 | 63 | 64 | class TexturePoint(Point): 65 | def __init__(self, screen, x, y, z, u, v): 66 | super(TexturePoint, self).__init__(screen, x, y, z) 67 | self.u = u 68 | self.v = v 69 | 70 | def add(self, x=0, y=0, z=0, u=0, v=0): 71 | self.x += x 72 | self.y += y 73 | self.z += z 74 | self.u += u 75 | self.v += v 76 | 77 | 78 | def show_face(): 79 | half_scr_x = int(scr_x/2) 80 | half_scr_y = int(scr_y/2) 81 | texture_img = Image.open('african_head_diffuse.tga') 82 | texture = texture_img.load() 83 | f = open('face.obj', 'r') 84 | lines = f.read() 85 | points = [] 86 | textures = [] 87 | screen = Screen(scr_x, scr_y) 88 | for line in lines.split('\n'): 89 | try: 90 | v, x, y, z = re.split('\s+', line) 91 | except ValueError: 92 | continue 93 | if v == 'v': 94 | x = int((float(x) + 1) * half_scr_x) 95 | y = int((float(y) + 1) * half_scr_y) 96 | z = float(z) + 1 97 | points.append((x, y, z)) 98 | if v == 'vt': 99 | u = float(x) * texture_img.width 100 | v = (1 - float(y)) * texture_img.height 101 | textures.append((u, v)) 102 | if v == 'f': 103 | indexes = [[int(j)-1 for j in i.split('/')] for i in (x, y, z)] 104 | tr_points = [] 105 | for i in range(3): 106 | params = points[indexes[i][0]] + textures[indexes[i][1]] 107 | tr_points.append(screen.point(*params)) 108 | screen.triangle(tr_points, texture) 109 | screen.img.show() 110 | 111 | show_face() 112 | -------------------------------------------------------------------------------- /sokoban.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import telebot 3 | from telebot import types 4 | from sokoban_config import token 5 | 6 | bot = telebot.TeleBot(token) 7 | 8 | 9 | def show_map(gmap): 10 | gmap_width = str(gmap.find(u'█\n') + 1) 11 | btn = types.InlineKeyboardButton 12 | markup = types.InlineKeyboardMarkup(row_width=2) 13 | markup.add( 14 | btn('', callback_data='0'), 15 | btn(u'⬆', callback_data='-' + gmap_width), 16 | btn(u'⬅', callback_data='-1'), 17 | btn(u'➡', callback_data='1'), 18 | btn('', callback_data='0'), 19 | btn(u'⬇', callback_data=gmap_width), 20 | ) 21 | return { 22 | 'text': '' + gmap + '', 23 | 'parse_mode': 'html', 24 | 'reply_markup': markup 25 | } 26 | 27 | 28 | @bot.message_handler(content_types=['text']) 29 | def any_msg(message): 30 | gmap = u""" 31 | ██████████ 32 | ██████ . █ 33 | █ ◯☿◯ ◯ █ 34 | █ ..██ 35 | ██████████ 36 | """.replace('\n ', '\n') 37 | bot.send_message(message.chat.id, **show_map(gmap)) 38 | 39 | 40 | def replace_on_map(game_map, pos, char): 41 | return game_map[:pos] + char + game_map[pos + 1:] 42 | 43 | 44 | @bot.callback_query_handler(func=lambda call: True) 45 | def callback_inline(call): 46 | 47 | if call.message: 48 | gmap = call.message.text 49 | movement = int(call.data) 50 | 51 | pos = gmap.find(u'☿') 52 | if pos < 0: 53 | pos = gmap.find(u'♆') 54 | 55 | new_pos = pos + movement 56 | new_place = gmap[new_pos] 57 | next_place = gmap[new_pos + movement] 58 | 59 | if new_place in (' ', '.') or (new_place in (u'◯', u'◉') and next_place in (' ', '.')): 60 | if new_place in (u'◯', u'◉'): 61 | gmap = replace_on_map(gmap, new_pos + movement, u'◉' if next_place == '.' else u'◯') 62 | gmap = replace_on_map(gmap, pos, ' ' if gmap[pos] == u'☿' else '.') 63 | gmap = replace_on_map(gmap, new_pos, u'☿' if new_place in (' ', u'◯') else u'♆') 64 | 65 | if gmap != call.message.text: 66 | bot.edit_message_text( 67 | chat_id=call.message.chat.id, 68 | message_id=call.message.message_id, 69 | **show_map(gmap) 70 | ) 71 | 72 | 73 | bot.polling(none_stop=True) 74 | -------------------------------------------------------------------------------- /sokoban_config.py: -------------------------------------------------------------------------------- 1 | token = '123456789:XXXXXXXXXXXXXXXXXX' 2 | --------------------------------------------------------------------------------