Console 2048 (procrastination)

This is the place to post any code that you want to share with the community. Only completed scripts should be posted here.

Console 2048 (procrastination)

Postby Mekire » Mon Apr 28, 2014 8:12 am

Wrote this up when I could have been doing something more productive.
It is just an implementation of 2048 that runs in the terminal.
Annoying wasd controls that must be confirmed with enter.
I'm sure I overcomplicated things but I doubt I will dedicate much more time to it.

Try non-standard grids by passing command line arguments (if none are passed you get default 4x4).

For example the following would give you a 3x3 grid:
Code: Select all
python console2048.py 3 3

Repository here:
https://github.com/Mekire/console-2048

Program in its entirety below (see git repo for more up to date color version):
Code: Select all
from __future__ import print_function

import os
import sys
import copy
import random
import functools

#Python 2/3 compatibility.
if sys.version_info[0] == 2:
    range = xrange
    input 
= raw_input


def _getch_windows
(prompt):
    """
    Windows specific version of getch.  Special keys like arrows actually post
    two key events.  If you want to use these keys you can create a dictionary
    and return the result of looking up the appropriate second key within the
    if block.
    """
 
    print
(prompt, end="")
    key = msvcrt.getch()
    if ord(key) == 224:
        key = msvcrt.getch()
        return key
    print
(key)
    return key


def _getch_linux
(prompt):
    """Linux specific version of getch."""
    print(prompt, end="")
    sys.stdout.flush()
    fd = sys.stdin.fileno()
    old = termios.tcgetattr(fd)
    new = termios.tcgetattr(fd)
    new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
    new[6][termios.VMIN] = 1
    new
[6][termios.VTIME] = 0
    termios
.tcsetattr(fd, termios.TCSANOW, new)
    char = None
    try
:
        char = os.read(fd, 1)
    finally:
        termios.tcsetattr(fd, termios.TCSAFLUSH, old)
    print(char)
    return char


#Set version of getch to use based on operating system.
if sys.platform[:3] == 'win':
    import msvcrt
    getch 
= _getch_windows
elif sys
.platform[:3] == 'lin':
    import termios
    getch 
= _getch_linux


def push_row
(row, left=True):
    """Push all tiles in one row; like tiles will be merged together."""
    row = row[:] if left else row[::-1]
    new_row = [item for item in row if item]
    for i in range(len(new_row)-1):
        if new_row[i] and new_row[i] == new_row[i+1]:
            new_row[i], new_row[i+1:] = new_row[i]*2, new_row[i+2:]+[""]
    new_row += [""]*(len(row)-len(new_row))
    return new_row if left else new_row[::-1]


def get_column(grid, column_index):
    """Return the column from the grid at column_index  as a list."""
    return [row[column_index] for row in grid]


def set_column(grid, column_index, new):
    """
    Replace the values in the grid at column_index with the values in new.
    The grid is changed inplace.
    """
    for i,row in enumerate(grid):
        row[column_index] = new[i]


def push_all_rows(grid, left=True):
    """
    Perform a horizontal shift on all rows.
    Pass left=True for left and left=False for right.
    The grid will be changed inplace.
    """
    for i,row in enumerate(grid):
        grid[i] = push_row(row, left)


def push_all_columns(grid, up=True):
    """
    Perform a vertical shift on all columns.
    Pass up=True for up and up=False for down.
    The grid will be changed inplace.
    """
    for i,val in enumerate(grid[0]):
        column = get_column(grid, i)
        new = push_row(column, up)
        set_column(grid, i, new)


def get_empty_cells(grid):
    """Return a list of coordinate pairs corresponding to empty cells."""
    empty = []
    for j,row in enumerate(grid):
        for i,val in enumerate(row):
            if not val:
                empty.append((j,i))
    return empty


def any_possible_moves
(grid):
    """Return True if there are any legal moves, and False otherwise."""
    if get_empty_cells(grid):
        return True
    for row in grid
:
        if any(row[i]==row[i+1] for i in range(len(row)-1)):
            return True
    for i
,val in enumerate(grid[0]):
        column = get_column(grid, i)
        if any(column[i]==column[i+1] for i in range(len(column)-1)):
            return True
    return False


def get_start_grid
(cols=4, rows=4):
    """Create the start grid and seed it with two numbers."""
    grid = [[""]*cols for i in range(rows)]
    for i in range(2):
        empties = get_empty_cells(grid)
        y,= random.choice(empties)
        grid[y][x] = random.choice((2,2,2,4))
    return grid


def prepare_next_turn
(grid):
    """
    Spawn a new number on the grid; then return the result of
    any_possible_moves after this change has been made.
    """
    empties = get_empty_cells(grid)
    y,= random.choice(empties)
    grid[y][x] = random.choice((2,2,2,4))
    return any_possible_moves(grid)


def print_grid(grid):
    """Print a pretty grid to the screen."""
    print("")
    wall = "+------"*len(grid[0])+"+"
    print(wall)
    for row in grid:
        meat = "|".join("{:^6}".format(val) for val in row)
        print("|{}|".format(meat))
        print(wall)


def main():
    """
    Get user input.
    Update game state.
    Display updates to user.
    """
    functions = {"a" : functools.partial(push_all_rows, left=True),
                 "d" : functools.partial(push_all_rows, left=False),
                 "w" : functools.partial(push_all_columns, up=True),
                 "s" : functools.partial(push_all_columns, up=False)}
    grid = get_start_grid(*map(int,sys.argv[1:]))
    print_grid(grid)
    done = False
    while not done
:
        grid_copy = copy.deepcopy(grid)
        get_input = getch("Enter direction (w/a/s/d): ").decode()
        if get_input in functions:
            functions[get_input](grid)
        elif get_input == "q":
            done = True
            break
        else
:
            print("\nInvalid choice.")
            continue
        if grid 
!= grid_copy:
            if not prepare_next_turn(grid):
                print_grid(grid)
                print("You Lose!")
                break
        print_grid
(grid)
    print("Thanks for playing.")


if __name__ == "__main__":
    main()

-Mek
Last edited by Mekire on Sat May 10, 2014 5:00 pm, edited 2 times in total.
Reason: Updated code with current version.
User avatar
Mekire
 
Posts: 1012
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Console 2048 (procrastination)

Postby metulburr » Tue Apr 29, 2014 4:45 am

To be honest, im surprised you didnt write it in pygame. I made a pull request adding input without enter though. Hitting enter every time drove me nuts.

I havent even heard of this game or the like before. Im still deciphering it to understand how to play it.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1501
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Console 2048 (procrastination)

Postby Mekire » Tue Apr 29, 2014 6:36 am

Merged.
Pretty amazed you haven't heard of it.
Rules are really basic. Tiles slide in the direction pressed and if two of the same numbers run in to each other they combine. Every turn a 2, or by a slightly lower chance a 4, will spawn in an empty cell. Game is over when there are no legal moves.

You can play the actual game online here:
http://gabrielecirulli.github.io/2048/
and his source is here:
https://github.com/gabrielecirulli/2048

I didn't make a Pygame version because it was just a distraction while at work.
I considered implementing getch but was similarly lazy, so thanks for that.

-Mek
User avatar
Mekire
 
Posts: 1012
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Console 2048 (procrastination)

Postby metulburr » Tue Apr 29, 2014 6:42 am

if two of the same numbers run in to each other they combine

ok that was the part i wasnt catching on to.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1501
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Console 2048 (procrastination)

Postby Mekire » Tue Apr 29, 2014 9:25 am

Made a couple minor changes. The windows getch would crash if it got a special key (like an arrow key) because they post two events as one.
Seems to be working quite nicely on both my OS now.
Now to completely over do it with ansi color codes. :p

-Mek
User avatar
Mekire
 
Posts: 1012
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Console 2048 (procrastination)

Postby metulburr » Tue Apr 29, 2014 11:11 am

ah ok, i didnt catch that. Good to know.

Now to completely over do it with ansi color codes

:D
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1501
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Console 2048 (procrastination)

Postby Mekire » Tue Apr 29, 2014 12:42 pm

Color terminal version added.
Tested successfully on Linux Mint py2/3 and Windows 7 py2/3.
Uses the Colorama package to make Windows behave correctly (included in repo).

-Mek
User avatar
Mekire
 
Posts: 1012
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Console 2048 (procrastination)

Postby Kebap » Tue Apr 29, 2014 5:04 pm

nice job! how about a 2048 solver? :mrgreen:
Learn: How To Ask Questions The Smart Way
Join the #python-forum IRC channel on irc.freenode.net and chat with uns directly!
Kebap
 
Posts: 397
Joined: Thu Apr 04, 2013 1:17 pm
Location: Germany, Europe


Return to Completed Scripts

Who is online

Users browsing this forum: No registered users and 2 guests