#!/usr/bin/python3
import gi, random, copy
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, Pango
import cairo

COLORS = {0:(0.8,0.75,0.7),2:(0.93,0.89,0.85),4:(0.93,0.87,0.78),8:(0.95,0.69,0.47),
          16:(0.96,0.58,0.39),32:(0.96,0.49,0.37),64:(0.96,0.37,0.23),
          128:(0.93,0.82,0.45),256:(0.93,0.80,0.38),512:(0.93,0.78,0.31),
          1024:(0.93,0.76,0.25),2048:(0.93,0.74,0.18)}

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='2048')
        self.set_default_size(420, 480)
        self.set_resizable(False)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        vbox.set_margin_top(10); vbox.set_margin_bottom(10)
        vbox.set_margin_start(10); vbox.set_margin_end(10)
        self.add(vbox)
        hb = Gtk.Box(spacing=10)
        self.score_lbl = Gtk.Label()
        self.score_lbl.set_markup('<span size="large" weight="bold">Score: 0</span>')
        hb.pack_start(self.score_lbl, True, True, 0)
        nb = Gtk.Button(label='New Game'); nb.connect('clicked', lambda *_: self.new_game())
        hb.pack_start(nb, False, False, 0)
        vbox.pack_start(hb, False, False, 0)
        self.da = Gtk.DrawingArea()
        self.da.set_size_request(400, 400)
        self.da.connect('draw', self.draw)
        vbox.pack_start(self.da, True, True, 0)
        self.connect('key-press-event', self.key)
        self.new_game()
    def new_game(self):
        self.board = [[0]*4 for _ in range(4)]
        self.score = 0
        self.add_tile(); self.add_tile()
        self.score_lbl.set_markup('<span size="large" weight="bold">Score: 0</span>')
        self.da.queue_draw()
    def add_tile(self):
        empty = [(r,c) for r in range(4) for c in range(4) if self.board[r][c]==0]
        if empty:
            r,c = random.choice(empty)
            self.board[r][c] = 4 if random.random()<0.1 else 2
    def slide_row(self, row):
        tiles = [x for x in row if x]
        merged = []
        skip = False
        for i in range(len(tiles)):
            if skip: skip=False; continue
            if i+1<len(tiles) and tiles[i]==tiles[i+1]:
                merged.append(tiles[i]*2); self.score+=tiles[i]*2; skip=True
            else: merged.append(tiles[i])
        return merged + [0]*(4-len(merged))
    def move(self, direction):
        old = copy.deepcopy(self.board)
        b = self.board
        if direction=='Left':
            for r in range(4): b[r]=self.slide_row(b[r])
        elif direction=='Right':
            for r in range(4): b[r]=self.slide_row(b[r][::-1])[::-1]
        elif direction=='Up':
            for c in range(4):
                col=[b[r][c] for r in range(4)]
                col=self.slide_row(col)
                for r in range(4): b[r][c]=col[r]
        elif direction=='Down':
            for c in range(4):
                col=[b[r][c] for r in range(4)]
                col=self.slide_row(col[::-1])[::-1]
                for r in range(4): b[r][c]=col[r]
        if b!=old: self.add_tile()
        self.score_lbl.set_markup(f'<span size="large" weight="bold">Score: {self.score}</span>')
        self.da.queue_draw()
    def key(self, w, e):
        kn = Gdk.keyval_name(e.keyval)
        if kn in ('Left','Right','Up','Down'): self.move(kn)
    def draw(self, w, cr):
        alloc = w.get_allocation()
        W,H = alloc.width, alloc.height
        cell = min(W,H)/4
        cr.set_source_rgb(0.73,0.68,0.63); cr.paint()
        for r in range(4):
            for c in range(4):
                val = self.board[r][c]
                col = COLORS.get(val, (0.37,0.35,0.32))
                cr.set_source_rgb(*col)
                cr.rectangle(c*cell+4, r*cell+4, cell-8, cell-8)
                cr.fill()
                if val:
                    cr.set_source_rgb(0.35 if val>4 else 0.47, 0.35 if val>4 else 0.43, 0.33 if val>4 else 0.40)
                    fsize = 32 if val<100 else (26 if val<1000 else 20)
                    cr.set_font_size(fsize)
                    txt = str(val)
                    ext = cr.text_extents(txt)
                    cr.move_to(c*cell+cell/2-ext.width/2, r*cell+cell/2+ext.height/2)
                    cr.show_text(txt)

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
