from src.model.player import Player
from src.model.beam import Beam
from src.model.weaponBeam import WeaponBeam
from src.model.projectileExploding import ProjectileExploding
from src.model.enemy import Enemy
from src.model.healthPack import HealthPack
from src.model.ammoPack import AmmoPack
from src.model.upgrade import Upgrade
from src.model.asteroid import Asteroid
import math
import pygame
import random

class GameState():
    def __init__(self, window_size: tuple, scores, sounds):
        self.scores = scores
        self.sounds = sounds
        self.player = Player(465, 800, window_size, sounds)
        self.enemies = []
        self.projectiles = []
        self.explosions = []
        self.health_packs = []
        self.ammo_packs = []
        self.upgrades = []
        self.asteroids = []
        self.player_score = 0
        self.wave_counter = 0
        self.wave_length = 0
        self.wave_delay = 0
        self.enemies_spawned = 0
        self.window_size = window_size
        self.window_width = window_size[0]
        self.window_height = window_size[1]
        self.pause = False
        self.wave_spawned = False
        self.game_lost = False
        self.game_start_timer = 180
        self.asteroid_timer = 1200
        self.death_timer = 300

    def get_input(self):
        pygame.event.pump()
        keys = pygame.key.get_pressed()
        mouse = pygame.mouse.get_pressed()
        if keys[pygame.K_a]:  # left
            self.player.move_left()

        if keys[pygame.K_d]:  # right
            self.player.move_right()

        if keys[pygame.K_w]:  # up
            self.player.move_up()

        if keys[pygame.K_s]:  # down
            self.player.move_down()

        if mouse[0]:
            if isinstance(self.player.primary_weapon, WeaponBeam):
                proj = self.player.shoot_primary()
                self.player.primary_weapon.shooting = True
                if proj is not None:
                    self.projectiles.append(proj)
            else:
                proj = self.player.shoot_primary()
                if proj is not None:
                    self.projectiles.append(proj)

        elif isinstance(self.player.primary_weapon, WeaponBeam):
            self.player.primary_weapon.shooting = False

        if mouse[2]:
            projs = self.player.shoot_secondary()
            if projs is not None:
                for proj in projs:
                    self.projectiles.append(proj)

        if keys[pygame.K_ESCAPE]:
            self.pause = True

    def get_distance(self, x1, y1, x2, y2):
        return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

    def spawn_enemy(self):
        if len(self.enemies) == 0 and self.wave_spawned:
            if self.wave_delay > 0:
                self.wave_delay -= 1

            else:
                self.enemies_spawned = 0
                self.wave_spawned = False
                self.wave_delay = 120
                self.wave_counter += 1
                self.wave_length += 1

        elif not self.wave_spawned:

            if self.enemies_spawned == self.wave_length:
                self.wave_spawned = True

            while self.enemies_spawned < self.wave_length:
                if self.wave_counter < 5:
                    rarity = random.choice(["common", "uncommon"])
                if self.wave_counter >= 5 and self.wave_counter < 10:
                    rarity = random.choice(["common", "uncommon", "rare"])
                if self.wave_counter >= 10 and self.wave_counter < 15:
                    rarity = random.choice(["common", "uncommon", "rare", "epic"])
                if self.wave_counter >= 15:
                    rarity = random.choice(["common", "uncommon", "rare", "epic", "legendary"])

                enemy = Enemy(random.randrange(100, 850), random.randrange(-400, -100), 2, self.window_size, rarity, self.wave_counter)
                is_valid_pos = True
                for existing_enemy in self.enemies:
                    if self.get_distance(existing_enemy.pos_x, existing_enemy.pos_y, enemy.pos_x, enemy.pos_y) < 200:
                        is_valid_pos = False
                        return

                if is_valid_pos:
                    self.enemies_spawned += 1
                    self.enemies.append(enemy)

    def projectile_enemy_collision_check(self):
        for proj in self.projectiles:

            if not isinstance(proj, Beam):
                proj.update()
                if proj.disabled:
                    if proj in self.projectiles:
                        self.projectiles.remove(proj)

                elif proj.owner == "enemy":
                    if proj.collision(self.player):
                        self.player.hit(proj.damage)
                        proj.hit()

                elif proj.owner == "player":
                    for enemy in self.enemies:
                        if proj.tracking and proj.tracking_obj is None and not enemy.tracked:
                            proj.tracking_obj = enemy
                            enemy.tracked = True
                            enemy.tracked_obj = proj
                        if proj.collision(enemy):
                            enemy.hit(proj.damage)
                            proj.hit()
                            if isinstance(proj, ProjectileExploding):
                                self.sounds.explosion.play()
                                self.explosions.append(proj.explode())

            else:
                if not proj.disabled:
                    for enemy in self.enemies:
                        if proj.collision(enemy):
                            enemy.hit(proj.damage)
                            proj.hit()

    def explosion_enemy_collision_check(self):
        for explosion in self.explosions:
            explosion.update()
            if explosion.disabled:
                if explosion in self.explosions:
                    self.explosions.remove(explosion)

            if explosion.active:
                for enemy in self.enemies:
                    if explosion.collision(enemy):
                        enemy.hit(explosion.damage)

                explosion.active = False

    def player_enemy_collision_check(self):
        for enemy in self.enemies:
            if enemy.collision(self.player):
                self.player.hit(self.player.health_max / 2 + 10)
                enemy.hit(enemy.health_max)

    def drop_item(self, pos_x, pos_y, rarity):
        rand_num = random.randint(0, 100)
        if rand_num >= 40 and rand_num < 60:
            self.health_packs.append(HealthPack(pos_x, pos_y, self.window_size))
        if rand_num >= 60 and rand_num < 80:
            self.ammo_packs.append(AmmoPack(pos_x, pos_y, self.window_size))
        if rand_num >= 80:
            if rarity != "common":
                self.upgrades.append(Upgrade(pos_x, pos_y, self.window_size, rarity))

    def update_health_packs(self):
        for health_pack in self.health_packs:
            health_pack.update()
            if health_pack.disabled:
                if health_pack in self.health_packs:
                    self.health_packs.remove(health_pack)
            elif health_pack.collision(self.player):
                if self.player.health_cur <= self.player.health_max * 4 / 5:
                    self.player.health_cur += self.player.health_max/5
                else:
                    self.player.health_cur = self.player.health_max

                self.sounds.health_pack.play()
                health_pack.disabled = True

    def update_ammo_packs(self):
        for ammo_pack in self.ammo_packs:
            ammo_pack.update()
            if ammo_pack.disabled:
                if ammo_pack in self.ammo_packs:
                    self.ammo_packs.remove(ammo_pack)
            elif ammo_pack.collision(self.player):
                self.player.refil_ammo()
                self.sounds.reload.play()
                ammo_pack.disabled = True

    def update_upgrades(self):
        for upgrade in self.upgrades:
            upgrade.update()
            if upgrade.disabled:
                if upgrade in self.upgrades:
                    self.upgrades.remove(upgrade)
            elif upgrade.collision(self.player):
                self.sounds.upgrade.play()
                upgrade.random_upgrade(self.player)
                upgrade.disabled = True

    def update_asteroids(self):
        for ast in self.asteroids:
            ast.update()
            if ast.disabled:
                if ast in self.asteroids:
                    self.asteroids.remove(ast)

            if ast.collision(self.player):
                self.player.hit(ast.health)
                ast.hit(self.player.health_cur)

            for proj in self.projectiles:
                if proj.owner == "player":
                    if proj.collision(ast):
                        proj.hit()
                        ast.hit(proj.damage)

    def spawn_asteroid(self):
        x = random.randint(0, 900)
        self.asteroids.append(Asteroid(x, -200, self.window_size, self.sounds))

    def update_score(self, rarity):
        if rarity == "common":
            self.player_score += math.ceil(1 * (1 + (0.1 * self.wave_counter)))
        if rarity == "uncommon":
            self.player_score += math.ceil(2 * (1 + (0.1 * self.wave_counter)))
        if rarity == "rare":
            self.player_score += math.ceil(3 * (1 + (0.1 * self.wave_counter)))
        if rarity == "epic":
            self.player_score += math.ceil(4 * (1 + (0.1 * self.wave_counter)))
        if rarity == "legendary":
            self.player_score += math.ceil(5 * (1 + (0.1 * self.wave_counter)))

    def update_enemies(self):
        for enemy in self.enemies:
            enemy.update()
            if enemy.cooldown <= 0:
                randint = random.randint(0, 100)
                if randint <= 30:
                    self.projectiles.append(enemy.shoot())
                    enemy.cooldown = 60
                else:
                    enemy.cooldown = 30
            else:
                enemy.cooldown -= 1
            if enemy.disabled:
                self.update_score(enemy.rarity)
                self.drop_item(enemy.pos_x, enemy.pos_y, enemy.rarity)
                self.explosions.append(enemy.death())
                if enemy in self.enemies:
                    self.enemies.remove(enemy)

    def update(self):
        if self.game_start_timer >= 1:
            self.game_start_timer -= 1

        else:
            if self.asteroid_timer > 0:
                self.asteroid_timer -= 1
            else:
                self.spawn_asteroid()
                self.asteroid_timer = random.randint(800, 1600)
            self.spawn_enemy()

            self.get_input()

            self.projectile_enemy_collision_check()
            self.explosion_enemy_collision_check()
            self.player_enemy_collision_check()
            self.update_enemies()
            self.update_upgrades()
            self.update_health_packs()
            self.update_ammo_packs()
            self.update_asteroids()

            self.player.update()

            if self.player.dead:
                if self.death_timer > 0:
                    self.death_timer -= 1
                else:
                    self.game_lost = True

                if self.death_timer == 150:
                    self.explosions.append(self.player.kill())

                self.scores.write_scores(self.player_score)


from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials #depreciated
import httplib2
import json
import os
import logging

def get_report():

    path1 = os.path.dirname(os.path.realpath(__file__))
    parentPath = os.path.dirname(path1)

    SPREADSHEET_ID = '1MUehUq5LVN3WjaT84Mve3zGG6sbn6x_citXygPclcK0'
    clear_RANGE_NAME = 'data!A2:E'

    SCOPES = ["https://www.googleapis.com/auth/tagmanager.readonly","https://www.googleapis.com/auth/spreadsheets"]
    KEY_FILE_LOCATION = os.path.join(parentPath,"creds","backlogger_bq.json")

    credentials = ServiceAccountCredentials.from_json_keyfile_name(KEY_FILE_LOCATION, scopes=SCOPES)

    http = httplib2.Http()
    http = credentials.authorize(http)

    vars = {"Mouseflow pages - All":"57",
             "Mouseflow pages - public":"21",
             "Mouseflow pages - adviser":"56"}

    gtm_service = build('tagmanager', 'v1', http=http)
    sheets_service = build('sheets', 'v4', http=http)
    sheet = sheets_service.spreadsheets()

    sheet.values().clear(spreadsheetId=SPREADSHEET_ID,
                                range=clear_RANGE_NAME).execute()

    for var, value in vars.items():
        variable = gtm_service.accounts().containers().variables().get(
            accountId='1090055821',
            containerId='6311504',
            variableId=value
        ).execute()
        clean = json.dumps(variable)
        parsed_json = json.loads(clean)
        j_list = parsed_json['parameter'][6]['list']

        for x in j_list:
            name = x['map'][0]['value']
            val = x['map'][1]['value']
            body = {"range": "data!A2:E",
                    "majorDimension": "ROWS",
                    "values": [
                    [var, name, val],
                    ],}
            sheet.values().append(spreadsheetId=SPREADSHEET_ID, range=clear_RANGE_NAME, body=body, valueInputOption='USER_ENTERED').execute()

if __name__ == '__main__':
    get_report()


import numpy as np
import time
from sys import argv
from sklearn import metrics
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.naive_bayes import MultinomialNB
from sklearn.multiclass import OneVsRestClassifier
from sklearn.multiclass import OneVsOneClassifier
from sklearn.linear_model import SGDClassifier
from sklearn.cluster import KMeans
from sklearn.svm import LinearSVC

'''
Take data file and label file as input
'''
data_list = open(argv[1]).read().split('\n')
label_list = open(argv[2]).read().split('\n')

'''
Split the data into train and test; split 90-10
'''
X_train, X_test, y_train, y_test = train_test_split(data_list, label_list, test_size=0.10, random_state=42)
'''
Count vectorizer and then get tf-idf features
'''
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(X_train)
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)

'''
Training a Naive Bayes Classfier
'''

clf = MultinomialNB().fit(X_train_tfidf, y_train)
#print(X_train_tfidf.shape)

'''
Test data, accuracy and confusion matrix
'''
docs_new = X_test
X_new_counts = count_vect.transform(docs_new)
X_new_tfidf = tfidf_transformer.transform(X_new_counts)
#print(X_new_tfidf.shape)
start = time.process_time()
predicted = clf.predict(X_new_tfidf)
end = time.process_time()
'''
for doc, category in zip(docs_new, predicted):
    print('%r => %s' % (doc, category))

'''
print('NB time', end-start)
print('NB Accuracy', np.mean(predicted == y_test))
print(metrics.classification_report(y_test, predicted, target_names=list(set(label_list))))
print('Confusion Matrix', metrics.confusion_matrix(y_test, predicted))

'''
OnevsRest
'''

clf_1 = OneVsRestClassifier(LinearSVC(random_state=0)).fit(X_train_tfidf, y_train)
start = time.process_time()
predicted_1 = clf_1.predict(X_new_tfidf)
end = time.process_time()
'''
for doc, category in zip(docs_new, predicted):
    print('%r => %s' % (doc, category))
'''

print('OnevsRest Time', end-start)
print('OnevsRest Accuracy', np.mean(predicted_1 == y_test))
print(metrics.classification_report(y_test, predicted_1, target_names=list(set(label_list))))
print('Confusion Matrix', metrics.confusion_matrix(y_test, predicted_1))

'''
OnevsOne
'''

clf_2 = OneVsOneClassifier(LinearSVC(C=100.)).fit(X_train_tfidf, y_train)
start = time.process_time()
predicted_2 = clf_2.predict(X_new_tfidf)
end = time.process_time()

print('OnevsOne Time', end-start)
print('OnevsOne Accuracy', np.mean(predicted_2 == y_test))
print(metrics.classification_report(y_test, predicted_2, target_names=list(set(label_list))))
print('Confusion Matrix', metrics.confusion_matrix(y_test, predicted_2))

'''
SGD
'''

clf_3 = SGDClassifier(loss='hinge', penalty='l2', alpha=1e-3, n_iter=5, random_state=42).fit(X_train_tfidf, y_train)

start = time.process_time()
predicted_3 = clf_3.predict(X_new_tfidf)
end = time.process_time()
print('SGD Time', end-start)
print('SGD Accuracy', np.mean(predicted_3 == y_test))
print(metrics.classification_report(y_test, predicted_3, target_names=list(set(label_list))))
print('Confusion Matrix', metrics.confusion_matrix(y_test, predicted_3))

'''
K means

clf_4 = KMeans(n_clusters=20, random_state=0).fit(X_train_tfidf)
start = time.process_time()
predicted_4 = clf_4.predict(X_new_tfidf)
end = time.process_time()
y_test = list(map(int, y_test))
print('KMeans Time', end-start)

print(len(y_test), len(predicted_4))
print('KMeans Accuracy', np.mean(predicted_4 == y_test))
print(list(set(label_list)))
print(metrics.classification_report(y_test, predicted_4, target_names=list(set(label_list))))
print('Confusion Matrix', metrics.confusion_matrix(y_test, predicted_4))
'''


'''
Created on 13/09/2013

@author: da.angulo39
'''
import os

import vtk

import braviz

os.chdir(r'C:\Users\da.angulo39\Documents')

r=braviz.readAndFilter.BravizAutoReader()

fibers=r.get('fibers','144',space='dartel')
writer=vtk.vtkPolyDataWriter()
writer.SetInputData(fibers)
writer.SetFileTypeToASCII()
writer.SetFileName('acii_fibers_.vtk')
writer.Update()


import torch
import torch.nn as nn

class Bottle(nn.Module):
    def forward(self, input):
        if len(input.size()) <= 2:
            return super(Bottle, self).forward(input)
        size = input.size()[:2]
        out = super(Bottle, self).forward(input.view(size[0]*size[1], -1))
        return out.contiguous().view(size[0], size[1], -1)

class BottleLinear(Bottle, nn.Linear):
    pass

def aeq(*args):
    """
    Assert all arguments have the same value
    """
    arguments = (arg for arg in args)
    first = next(arguments)
    assert all(arg == first for arg in arguments), \
        "Not all arguments have the same value: " + str(args)

def sequence_mask(lengths, max_len=None):
    """
    Creates a boolean mask from sequence lengths.
    """
    batch_size = lengths.numel()
    max_len = max_len or lengths.max()
    return (torch.arange(0, max_len)
            .type_as(lengths)
            .repeat(batch_size, 1)
            .lt(lengths.unsqueeze(1)))

class GlobalAttention(nn.Module):
    """
    Global attention takes a matrix and a query vector. It
    then computes a parameterized convex combination of the matrix
    based on the input query.

    Constructs a unit mapping a query `q` of size `dim`
    and a source matrix `H` of size `n x dim`, to an output
    of size `dim`.

    .. mermaid::

       graph BT
          A[Query]
          subgraph RNN
            C[H 1]
            D[H 2]
            E[H N]
          end
          F[Attn]
          G[Output]
          A --> F
          C --> F
          D --> F
          E --> F
          C -.-> G
          D -.-> G
          E -.-> G
          F --> G

    All models compute the output as
    :math:`c = \sum_{j=1}^{SeqLength} a_j H_j` where
    :math:`a_j` is the softmax of a score function.
    Then then apply a projection layer to [q, c].

    However they
    differ on how they compute the attention score.

    * Luong Attention (dot, general):
       * dot: :math:`score(H_j,q) = H_j^T q`
       * general: :math:`score(H_j, q) = H_j^T W_a q`

    * Bahdanau Attention (mlp):
       * :math:`score(H_j, q) = v_a^T tanh(W_a q + U_a h_j)`

    Args:
       dim (int): dimensionality of query and key
       coverage (bool): use coverage term
       attn_type (str): type of attention to use, options [dot,general,mlp]

    """
    def __init__(self, dim, coverage=False, attn_type="dot"):
        super(GlobalAttention, self).__init__()

        self.dim = dim
        self.attn_type = attn_type
        assert (self.attn_type in ["dot", "general", "mlp"]), (
                "Please select a valid attention type.")

        if self.attn_type == "general":
            self.linear_in = nn.Linear(dim, dim, bias=False)
        elif self.attn_type == "mlp":
            self.linear_context = BottleLinear(dim, dim, bias=False)
            self.linear_query = nn.Linear(dim, dim, bias=True)
            self.v = BottleLinear(dim, 1, bias=False)
        # mlp wants it with bias
        out_bias = self.attn_type == "mlp"
        self.linear_out = nn.Linear(dim*2, dim, bias=out_bias)

        self.sm = nn.Softmax()
        self.tanh = nn.Tanh()

        if coverage:
            self.linear_cover = nn.Linear(1, dim, bias=False)

    def score(self, h_t, h_s):
        """
        Args:
          h_t (`FloatTensor`): sequence of queries `[batch x tgt_len x dim]`
          h_s (`FloatTensor`): sequence of sources `[batch x src_len x dim]`

        Returns:
          :obj:`FloatTensor`:
           raw attention scores (unnormalized) for each src index
          `[batch x tgt_len x src_len]`

        """

        # Check input sizes
        src_batch, src_len, src_dim = h_s.size()
        tgt_batch, tgt_len, tgt_dim = h_t.size()
        aeq(src_batch, tgt_batch)
        aeq(src_dim, tgt_dim)
        aeq(self.dim, src_dim)

        if self.attn_type in ["general", "dot"]:
            if self.attn_type == "general":
                h_t_ = h_t.view(tgt_batch*tgt_len, tgt_dim)
                h_t_ = self.linear_in(h_t_)
                h_t = h_t_.view(tgt_batch, tgt_len, tgt_dim)
            h_s_ = h_s.transpose(1, 2)
            # (batch, t_len, d) x (batch, d, s_len) --> (batch, t_len, s_len)
            return torch.bmm(h_t, h_s_)
        else:
            dim = self.dim
            wq = self.linear_query(h_t.view(-1, dim))
            wq = wq.view(tgt_batch, tgt_len, 1, dim)
            wq = wq.expand(tgt_batch, tgt_len, src_len, dim)

            uh = self.linear_context(h_s.contiguous().view(-1, dim))
            uh = uh.view(src_batch, 1, src_len, dim)
            uh = uh.expand(src_batch, tgt_len, src_len, dim)

            # (batch, t_len, s_len, d)
            wquh = self.tanh(wq + uh)

            return self.v(wquh.view(-1, dim)).view(tgt_batch, tgt_len, src_len)

    def forward(self, input, context, context_lengths=None, coverage=None):
        """

        Args:
          input (`FloatTensor`): query vectors `[batch x tgt_len x dim]`
          context (`FloatTensor`): source vectors `[batch x src_len x dim]`
          context_lengths (`LongTensor`): the source context lengths `[batch]`
          coverage (`FloatTensor`): None (not supported yet)

        Returns:
          (`FloatTensor`, `FloatTensor`):

          * Computed vector `[tgt_len x batch x dim]`
          * Attention distribtutions for each query
             `[tgt_len x batch x src_len]`
        """

        # one step input
        if input.dim() == 2:
            one_step = True
            input = input.unsqueeze(1)
        else:
            one_step = False

        batch, sourceL, dim = context.size()
        batch_, targetL, dim_ = input.size()
        aeq(batch, batch_)
        aeq(dim, dim_)
        aeq(self.dim, dim)
        if coverage is not None:
            batch_, sourceL_ = coverage.size()
            aeq(batch, batch_)
            aeq(sourceL, sourceL_)

        if coverage is not None:
            cover = coverage.view(-1).unsqueeze(1)
            context += self.linear_cover(cover).view_as(context)
            context = self.tanh(context)

        # compute attention scores, as in Luong et al.
        align = self.score(input, context)

        if context_lengths is not None:
            mask = sequence_mask(context_lengths)
            mask = mask.unsqueeze(1)  # Make it broadcastable.
            align.data.masked_fill_(1 - mask, -float('inf'))

        # Softmax to normalize attention weights
        align_vectors = self.sm(align.view(batch*targetL, sourceL))
        align_vectors = align_vectors.view(batch, targetL, sourceL)

        # each context vector c_t is the weighted average
        # over all the source hidden states
        c = torch.bmm(align_vectors, context)

        # concatenate
        concat_c = torch.cat([c, input], 2).view(batch*targetL, dim*2)
        attn_h = self.linear_out(concat_c).view(batch, targetL, dim)
        if self.attn_type in ["general", "dot"]:
            attn_h = self.tanh(attn_h)

        if one_step:
            attn_h = attn_h.squeeze(1)
            align_vectors = align_vectors.squeeze(1)

            # Check output sizes
            batch_, dim_ = attn_h.size()
            aeq(batch, batch_)
            aeq(dim, dim_)
            batch_, sourceL_ = align_vectors.size()
            aeq(batch, batch_)
            aeq(sourceL, sourceL_)
        else:
            attn_h = attn_h.transpose(0, 1).contiguous()
            align_vectors = align_vectors.transpose(0, 1).contiguous()

            # Check output sizes
            targetL_, batch_, dim_ = attn_h.size()
            aeq(targetL, targetL_)
            aeq(batch, batch_)
            aeq(dim, dim_)
            targetL_, batch_, sourceL_ = align_vectors.size()
            aeq(targetL, targetL_)
            aeq(batch, batch_)
            aeq(sourceL, sourceL_)

        return attn_h, align_vectors


from math import inf
from collections import deque
import os
def move(n,board,pos,state):
    results=[]

    #parallel movement
    head=pos[0]
    tail=pos[1]
    movement=[(0,1),(1,0),(0,-1),(-1,0)]
    for dir in range(4):
        new_head=(head[0]+movement[dir][0],head[1]+movement[dir][1])
        new_tail=(tail[0]+movement[dir][0],tail[1]+movement[dir][1])

        if new_head[0]<=0 or new_head[0]>n or new_head[1]<=0 or new_head[1]>n or new_tail[0]<=0 or new_tail[0]>n or new_tail[1]<=0 or new_tail[1]>n:
            continue
        if board[new_head[0]-1][new_head[1]-1]==1 or board[new_tail[0]-1][new_tail[1]-1]==1:
            continue

        results.append(([new_head,new_tail],state))

    #state,axis,direction
    dy=[[[1,-1],[-1,1]],[[0,0],[0,0]]]
    dx=[[[0,0],[0,0]],[[-1,1],[1,-1]]]
    for axis in range(2):
        for direction in range(2):
            #회전한 이후의 좌표
            row,col=pos[axis]
            new_row=row+dy[state][axis][direction]
            new_col=col+dx[state][axis][direction]
            new_pos=(new_row,new_col)

            #회전 반경에 있는 좌표
            non_axis_row,non_axis_col=pos[1-axis]
            temp_row=non_axis_row+dy[state][axis][direction]
            temp_col=non_axis_col+dx[state][axis][direction]
            temp_pos=(temp_row,temp_col)

            #회전 이후의 좌표가 맵을 넘어서는 경우
            if new_row <=0 or new_row > n or new_col <=0 or new_col>n:
                continue
            if temp_row <=0 or temp_row > n or temp_col <=0 or temp_col>n:
                continue

            #회전 반경에 벽이 있는 경우
            if board[new_row-1][new_col-1]==1:
                continue
            if board[temp_row-1][temp_col-1]==1:
                continue

            results.append(([pos[axis],new_pos],1-state))
    return results

def solution(board):
    answer = 0
    n=len(board)

    queue=deque([([(1,1),(1,2)],0,0)])
    visited=[[(1,1),(1,2)]]

    while True:
        current_position,state,count=queue.popleft()
        if (n,n) in current_position:
            answer=count
            break

        candidate_movements=move(n,board,current_position,state)
        for candidate_position,state in candidate_movements:
            if candidate_position in visited:
                continue
            queue.append((candidate_position,state,count+1))
            visited.append(candidate_position)

    return answer

if __name__ == "__main__":
    board=[]
    with open("E:\\Codes\Code-Test-Preparing\\algorithm\\Tasks_By_Algorithms\\Implementation\\inputp60063.txt","r") as file:
        n=int(file.readline())
        board=[list(map(int,file.readline().split())) for _ in range(n)]
    print(solution(board))


import os
import sys
import argparse
import re
from TaskQueue import run
from ProcQueue import multi_proc
import time

# These are the different ways to invoke associated commands
help_types     = [ 'help', 'h', '-h', '--h', '--help' ]
generate_types = [ 'generate', 'gen' ]
clean_types    = [ 'clean' ]
list_types     = [ 'list', 'l' ]
tree_types     = [ 'tree', 'tr' ]
test_types     = [ 'test', 'tst', 'tdd', 'td' ]

cmd_string_list = [ \
    help_types, \
    generate_types, \
    clean_types, \
    list_types, \
    tree_types, \
    test_types \
    ]

# Store the available commands in a list, using the first elements from each action*_type list
action_types = [cmd_string_list[cmd_idx][0] for cmd_idx in range(len(cmd_string_list))]
# print( action_types )

# Command Strings
cmd_description_help     = "help command description"
cmd_description_generate = "generate command description"
cmd_description_clean    = "clean command description"
cmd_description_list     = "list command description"
cmd_description_tree     = "tree command description"
cmd_description_test     = "tdd command description"

cmd_description_msgs = [ \
    cmd_description_help, \
    cmd_description_generate, \
    cmd_description_clean, \
    cmd_description_list, \
    cmd_description_tree, \
    cmd_description_test \
    ]

cmd_usage_help     = "help command example"
cmd_usage_generate = "generate command example"
cmd_usage_clean    = "clean command example"
cmd_usage_list     = "list command example"
cmd_usage_tree     = "tree command example"
cmd_usage_test     = "tdd command example"

cmd_usage_msgs = [ \
    cmd_usage_help, \
    cmd_usage_generate, \
    cmd_usage_clean, \
    cmd_usage_list, \
    cmd_usage_tree, \
    cmd_usage_test \
    ]

cmd_help_help     = "help command help message"
cmd_help_generate = "generate command help message"
cmd_help_clean    = "clean command help message"
cmd_help_list     = "list command help message"
cmd_help_tree     = "tree command help message"
cmd_help_test     = "tdd command help message"

cmd_help_msgs = [ \
    cmd_help_help, \
    cmd_help_generate, \
    cmd_help_clean, \
    cmd_help_list, \
    cmd_help_tree, \
    cmd_help_test \
    ]

# Argument Strings
arg_help_module    = "How to use the module argument:"
arg_help_verbosity = "How to use the verbosity argument"
arg_help_types     = "How to use the types argument"
arg_help_recurse   = "How to use the --recurse flag"
arg_help_dry_run   = "How to use the --dry flag"
arg_help_runners   = "How to use the --runners flag"
arg_help_builds    = "How to use the --builds flag"
arg_help_results   = "How to use the --results flag"
arg_help_all       = "How to use the --all flag"
arg_help_venv      = "How to use the --venv flag"
arg_help_verbose   = "How to use the --verbose flag"
arg_help_suites    = "How to use the --suites flag"
arg_help_tests     = "How to use the --tests flag"
arg_help_scripts   = "How to use the --scripts flag"

# Argument "objects" is a reusable form
module_arg    = (('--module', '-module', '--m', '-m'),   {'dest':'module',    'help':arg_help_module,    'type':str,          })
mod_arg_reqd  = (('--module', '-module', '--m', '-m'),   {'dest':'module',    'help':arg_help_module,    'type':str, 'required':True })
verbosity_arg = (('--v', '-v'),                          {'dest':'verbosity', 'help':arg_help_verbosity, 'type':str,          })
recurse_flag  = (('--r', '-r'),                          {'dest':'recurse',   'help':arg_help_recurse,   'action':'store_true'})
dry_run_flag  = (('--dry', '-dry', '--d', '-d'),         {'dest':'dry',       'help':arg_help_dry_run,   'action':'store_true'})
runners_flag  = (('--runners', '-runners'),              {'dest':'runners',   'help':arg_help_runners,   'action':'store_true'})
builds_flag   = (('--builds', '-builds'),                {'dest':'builds',    'help':arg_help_builds,    'action':'store_true'})
results_flag  = (('--results', '-results'),              {'dest':'results',   'help':arg_help_results,   'action':'store_true'})
all_flag      = (('--all', '-all'),                      {'dest':'all',       'help':arg_help_all,       'action':'store_true'})
venv_flag     = (('--venv', '-venv'),                    {'dest':'venv',      'help':arg_help_venv,      'action':'store_true'})
verbose_flag  = (('--verbose', '-verbose'),              {'dest':'verbose',   'help':arg_help_verbose,   'action':'store_true'})
suites_flag   = (('--suites', '-suites'),                {'dest':'suites',    'help':arg_help_suites,    'action':'store_true'})
tests_flag    = (('--tests', '-tests'),                  {'dest':'tests',     'help':arg_help_tests,     'action':'store_true'})
scripts_flag  = (('--scripts', '-scripts'),              {'dest':'scripts',   'help':arg_help_scripts,   'action':'store_true'})

# Set the parameters used by cmod commands
generate_args = [ \
    mod_arg_reqd
    ]

workspace_args = [ \
    module_arg, \
    verbosity_arg, \
    recurse_flag \
    ]

tree_args = [ \
    module_arg, \
    suites_flag, \
    tests_flag \
    ]

clean_args = [ \
    module_arg, \
    recurse_flag, \
    dry_run_flag, \
    runners_flag, \
    builds_flag, \
    results_flag, \
    scripts_flag, \
    all_flag, \
    venv_flag, \
    verbose_flag
    ]

cmd_arg_types = [ \
    None, \
    generate_args, \
    clean_args, \
    workspace_args, \
    tree_args, \
    workspace_args \
    ]

def handle_help( args, configs ):
    print("handling help")

def parse_generate_args( args, configs ):
    parser = argparse.ArgumentParser( description=cmd_description_generate, usage=cmd_usage_generate )
    if generate_args is not None:
        [parser.add_argument( *arg[0], **arg[1] ) for arg in generate_args]
    # Add each generator key as a stored arg flag
    [parser.add_argument( '--'+key, dest=key, action='store_true' ) for key in configs["CMOD_FILE_GENERATORS"]]
    parsed_args = parser.parse_args( args )
    return parsed_args

def get_generator_module_types( parsed_arg_namespace ):
    # Access namespace as a dictionary and remove the only non-flag arg, 'modules'
    my_args_dict = vars(parsed_arg_namespace)
    module_dir = my_args_dict.pop("module", '.').strip("/")
    types = [ fileflag for fileflag in my_args_dict.keys() if my_args_dict[fileflag] == True ]
    return module_dir, types

def handle_generate( args, configs ):
    from FileGenerator import GeneratedFileDescriptor

    # TODO: Handle the case where the source is specified in the config without the file extension
    module_definition = configs["GLOBAL"]["default_module_def"]
    hook_source = os.path.splitext( os.path.normpath( configs[ module_definition ][ "file_gen_callbacks" ] ) )[0]
    hooks = __import__( hook_source, globals(), locals(), [], 0 )

    parsed_args = parse_generate_args( args, configs )
    module, types = get_generator_module_types( parsed_args )

    if len(types) == 0:
        module_def = configs["GLOBAL"]["default_module_def"]
        types = configs[module_def]["mod_def_list"].strip(' ').split(' ')
        module_name_hook = configs[ module_definition ][ "module_name_callback" ]
        print( module_name_hook )

        if hasattr( hooks, module_name_hook ):
            module_name_callback = getattr( hooks, module_name_hook )
            module = module_name_callback( module, configs )

    # print(types)
    file_generators = [ GeneratedFileDescriptor( module, configs[ "CMOD_FILE_GENERATORS" ][ file_def ], configs, hooks ) for file_def in types ]

    print( '\nwould generate:\n' )
    [ print( file_gen.gen_name( module, configs ) ) for file_gen in file_generators ]
    print('')

    answer = input( "Generate? Y/n: " )
    if answer != 'n':
        pass
    else:
        print( "canceled" )
        sys.exit()

    [ file_gen.gen_file( module, configs ) for file_gen in file_generators ]
    print('')

def handle_list( args, configs ):
    from Workspace import Workspace

    parser = argparse.ArgumentParser( description=cmd_description_list, usage=cmd_usage_list )
    if workspace_args is not None:
        [parser.add_argument( *arg[0], **arg[1] ) for arg in workspace_args]
    my_args = parser.parse_args( args )

    wksp = Workspace( args=my_args, configs=configs )
    wksp.print_module_names()

def handle_tree( args, configs ):
    from Tree import tree

    parser = argparse.ArgumentParser( description=cmd_description_tree, usage=cmd_usage_tree )
    if tree_args is not None:
        [parser.add_argument( *arg[0], **arg[1] ) for arg in tree_args]
    my_args = parser.parse_args( args )

    tree( my_args )

def handle_report( args, configs ):
    print("handling report\n")

def handle_stat( args, configs ):
    print("handling stat\n")

def handle_format( args, configs ):
    print("handling format\n")

def handle_analyze( args, configs ):
    print("handling analyze\n")

# Change this to a function that only operates on a
# single module
def handle_test( args, configs ):
    from Workspace import Workspace
    from Module import do_test_cycle

    parser = argparse.ArgumentParser( description=cmd_description_test, usage=cmd_usage_test )
    if workspace_args is not None:
        [parser.add_argument( *arg[0], **arg[1] ) for arg in workspace_args]
    my_args = parser.parse_args( args )

    wksp = Workspace( my_args, configs=configs )

    if wksp.num_modules > 20:
        from Module import do_test_cycle
        # Start a parallel process queue
        start = time.time()
        multi_proc( do_test_cycle, wksp.module_objs )
        print(f'Time taken = {time.time() - start:.2f}')
        # run( do_test_cycle, wksp.module_objs )
    else:
        # Runs tests with current process
        start = time.time()
        wksp.find_wksp_test_src_files()
        wksp.find_wksp_tests_and_groups()
        wksp.gen_wksp_test_runners()
        wksp.run_wksp_tests()
        wksp.calculate_test_result_totals()
        wksp.print_test_summary()
        print(f'Time taken = {time.time() - start:.10f}')

def handle_clean( args, configs ):
    from Clean import Cleaner

    parser = argparse.ArgumentParser( description=cmd_description_clean, usage=cmd_usage_clean )
    if clean_args is not None:
        [ parser.add_argument( *arg[0], **arg[1] ) for arg in clean_args ]
    my_args = parser.parse_args( args )

    cleaner = Cleaner( my_args, configs )
    cleaner.build_file_list()
    cleaner.do_cleaning()

action_handlers = [ \
    handle_help, \
    handle_generate, \
    handle_clean, \
    handle_list, \
    handle_tree,  \
    handle_test \
    ]

# Use the cmd index to lookup the cmd-associated data
cmd_handler_dict      = dict(zip(action_types, action_handlers))
cmd_args_dict         = dict(zip(action_types, cmd_arg_types))
cmd_descriptions_dict = dict(zip(action_types, cmd_description_msgs))
cmd_usage_msg_dict    = dict(zip(action_types, cmd_usage_msgs))
cmd_help_msg_dict     = dict(zip(action_types, cmd_help_msgs))

# TEST ME
def get_normalized_cmd( input_cmd ):
    idx = 0
    while input_cmd not in cmd_string_list[idx]:
        idx+=1
    # Return the first string - the full name of the command
    return cmd_string_list[idx][0]

def get_normalized_command( input_cmd ):
    if input_cmd in help_types:
        return action_types[0]
    elif input_cmd in generate_types:
        return action_types[1]
    elif input_cmd in clean_types:
        return action_types[2]
    elif input_cmd in list_types:
        return action_types[3]
    elif input_cmd in tree_types:
        return action_types[4]
    elif input_cmd in test_types:
        return action_types[5]
    else:
        print("invalid cmod command:", "\""+input_cmd+"\"" )
        sys.exit()

# This class collects everything needed for running cmod commands
class CmodCommand:
    def __init__( self, cmd ):
        self.handler = cmd_handler_dict[cmd]
        self.arg_types = cmd_args_dict[cmd]
        self.description = cmd_descriptions_dict[cmd]
        self.usage = cmd_usage_msg_dict[cmd]
        self.help_msg = cmd_help_msg_dict[cmd]

    # This where configs and arguments are passed to Cmod commands
    def run( self, args, configs ):
        self.handler( args, configs )

# This class applies the passed arguments and configs to a cmod command, then runs it
class ArgParser:
    def __init__( self, sys_args=None, configs=None ):
        self.args = sys_args
        self.configs = configs

    # Get the intended command, setup the command to run, and run it
    def cmod_entry( self ):
        cmd = get_normalized_command( self.args[0] )
        self.args.remove( self.args[0] )
        command = CmodCommand( cmd )
        command.run( self.args, self.configs )
        sys.exit()


"""
Construct a random Waffle Game
"""
from typing import List, Tuple, Dict, Set, Iterable
from random import choice, choices, randint, random, Random
from math import exp, log
from string import ascii_lowercase
from itertools import chain
from more_itertools import bucket
from collections import Counter
import numpy as np
from scipy.sparse import csr_matrix
from sklearn.neighbors import KDTree
import pycmsgen
from .clue import BOARD, SQUARE
from .waffle_solve import Waffle

def wordlist_to_vec(wordlist: List[str]):
    """
    Convert a wordlist to a 1-hot sparse encoding.

    Each row corresponds to a word.
    There are 26 * m columns where m is the word length
    """
    degree = len(wordlist[0])
    num = len(wordlist)
    mat = csr_matrix((degree * num * [1.0],
                      (list(chain(*(degree * [_]
                                    for _ in range(num)))),
                       list(chain(*[[ord(word[_]) - ord('a')
                                     + 26 * _
                                    for _ in range(degree)]
                                    for word in wordlist])))),
                      shape = (num, 26 * degree))
    return mat

def print_board(board: BOARD, answer: Dict[SQUARE, str]) -> Iterable[str]:
    """
    Format the answer
    """
    for rowcol in board:
        yield ''.join((answer[_] for _ in rowcol))

def letter_status(wordlist: List[str], board: BOARD) -> Dict[SQUARE,
                                                             np.ndarray]:
    """
    Calculate monographic distributions for each square

    For each square, first find the pair (continuent, position)
    that the square can be in.  For each one of these
    use the monographic statistics, taking the geometric mean
    over all such pairs.
    """
    degree = len(wordlist[0])
    num = len(wordlist)
    squares = set(chain(*board))
    counters = [Counter((word[idx] for word in wordlist))
                for idx in range(degree)]
    # Laplace's law of succession
    rprobs = [1 + np.array([ctr[ltr] for ltr in ascii_lowercase])
        for ctr in counters]
    probs = [_ / _.sum() for _ in rprobs]
    where = ((pidx, idx, square)
        for pidx, place in enumerate(board)
        for idx, square in enumerate(place))
    buckets = bucket(where, lambda _: _[2])
    out = {}
    for square in squares:
        lprobs = np.array([probs[idx]
            for _, idx, _ in buckets[square]])
        out[square] = lprobs.prod(axis=0) ** (1.0 / lprobs.shape[0])
    return out

def entropy(probs: np.ndarray) -> float:
    return - (np.log(probs) * probs).sum()

class MetropolisBoard:
    def __init__(self, wordlist: List[str],
                 board: BOARD,
                 distribute: bool = False,
                 hamming: bool = True,
                 use_entropy: bool = False,
                 seed: int | None = None):

        self._random = random.Random()
        self._random.seed(seed)
        self._wordlist = wordlist.copy()
        self._board = board.copy()
        self._subscore = self._hamming if hamming else self._basic
        if hamming:
            self._tree = KDTree(
                wordlist_to_vec(self._wordlist).toarray(),
                leaf_size=30, metric='manhattan')
        else:
            self._lookup = set(self._wordlist)
        self._distribute = distribute
        self._squares = list(sorted(set(chain(*board))))
        if distribute:
            self._distr = letter_status(wordlist, board)
        else: # Uniform distribution
            unif = np.ones(len(ascii_lowercase)) / len(ascii_lowercase)
            self._distr = {square: unif for square in self._squares}
        self._assign = None
        # Choose square of highest entropy
        if use_entropy:
            wgts = [entropy(self._distr[square])
                for square in self._squares]
            self._weights = [_/ sum(wgts) for _ in wgts]
        else:
            self._weights = [1/len(self._squares)
                for _ in self._squares]

    def _basic(self, words: List[str]) -> float:
        return sum((_ not in self._lookup for _ in words))

    def _hamming(self, words: List[str]) -> float:

        distances, _ = self._tree.query(
            wordlist_to_vec(words).toarray(),
            k=1, return_distance=True)
        return 0.5 * distances.sum()

    def _score(self) -> float:
        words = [''.join((self._assign[_] for _ in elt))
            for elt in self._board]
        addend = len(words) - len(set(words))
        return addend + self._subscore(words)

    def sample(self,
               temperature: float,
               burnin: int = 1000,
               trace: int = 0):

        # initial guess
        self._assign = {square: self._random.choices(
            ascii_lowercase,
            weights = self._distr[square])[0]
            for square in self._squares}
        tries = 0
        taken = 0
        score = self._score()

        back = {val: key for key, val in enumerate(ascii_lowercase)}

        while True:
            tries += 1
            if trace > 0 and tries % trace == 0:
                print(f"Step {tries}, taken {taken}, score = {score}")
            if tries > burnin and score == 0.0:
                return self._assign

            # Choose a random square and change it to a random
            # letter
            where = self._random.choices(self._squares, weights = self._weights)[0]
            old = self._assign[where]
            how = old
            while True:
                how = self._random.choices(ascii_lowercase,
                    weights = self._distr[where])[0]
                if how != old:
                    break
            self._assign[where] = how
            new_score = self._score()
            if self._distribute: # Non reversible chain
                # fix up the score
                # N = # of squares
                # pi(y) = exp(-E(y)/T) / Z
                # K(x,y) = 1/N * Prob(new letter)
                # Prob(new letter) = P(new)/(1-Prob(old))
                tab = self._distr[where]
                t_how = tab[back[how]]
                t_old = tab[back[old]]
                numer = t_old * (1.0 - t_old)
                denom = t_how * (1.0 - t_how)
                supp = log(numer / denom) * temperature
            else:
                supp = 0.0 # Reversible
            delta_score = new_score - score + supp
            if (delta_score <= 0
                or delta_score <= - temperature * log(random())):
                # Accept this move
                score = new_score
                taken += 1
            else:
                # reject, and put back the old state
                self._assign[where] = old

    def word_sample(self,
                    temperature: float,
                    burnin: int = 1000,
                    trace: int = 0):
        values = self.sample(temperature,
            burnin = burnin,
            trace = trace)

        # Convert to words
        return [''.join((values[_] for _ in elt))
                for elt in self._board]

def cms_sample(waf: Waffle, seed: int | None = None) -> Iterable[Tuple[str, 0]]:

    board = waf._board
    solver = pycmsgen.Solver(seed = seed)
    waf._basic()
    solver.add_clauses(waf._cnf.clauses)
    while True:
        status = solver.solve()
        if not status:
            break
        pos = [waf._pool.obj(_) for _ in solver.get_model() if _ > 0]
        yield [_[1: ] for _ in pos if _ is not None and _[0] == 'w']


#!/usr/bin/env python3
''' PySpaceX, a simple game shooter made in Pygame

Inspired by the wonderful KidsCanCode videos on youtube
https://www.youtube.com/channel/UCNaPQ5uLX5iIEHUCLmfAgKg
'''
import os
import sys
import glob
import random
import pygame as pg
from webcolors import name_to_rgb as rgb

from settings import *
from sprites.Explosion import Explosion
from sprites.Mob import Mob
from sprites.Player import Player
from sprites.PowerUp import PowerUp
from sprites.Starfield import Starfield

class Game(object):
    def __init__(self):
        pg.init()
        pg.mixer.init()
        self.screen = pg.display.set_mode((WIDTH, HEIGHT))
        pg.display.set_caption(TITLE)
        self.clock = pg.time.Clock()
        # Load graphics and sound
        self._load_gfx()
        self._load_snd()
        # Running indicates the game is in an active state
        self.running = True

    def new(self):
        ''' start up a brand new game '''
        # Create group and sprites
        self.all_sprites = pg.sprite.Group()
        self.mobs = pg.sprite.Group()
        self.bullets = pg.sprite.Group()
        self.powerups = pg.sprite.Group()
        self.starfield = Starfield(200)

        self.player = Player(self)
        self.all_sprites.add(self.player)
        self.score = 0

        for _ in range(8):
            self._respawn_mob()

        # Play background music and let's get started !
        pg.mixer.music.play(loops=-1)
        self.run()

    def run(self):
        ''' main game loop after initialization '''
        # Playing indicates the game has actually started
        self.playing = True
        while self.playing:
            self.clock.tick(FPS)
            self.draw()
            self.events()
            self.update()
            # Loop condition to end the game
            if self.player.lives == 0 and not self.explosion.alive():
                self.playing = False

    def draw(self):
        ''' draw objects on screen '''
        self.screen.fill((0, 0, 0))
        self.starfield.draw_stars(self.screen)
        self.all_sprites.draw(self.screen)
        Game.draw_text(self.screen, "Score = {}".format(self.score),
                       size=20,
                       pos=(WIDTH / 2, 10))
        Game.draw_shield_bar(self.screen, 5, 5, self.player.shield)
        Game.draw_lives(self.screen, 700, 5,
                        self.player.lives, self.player_mini_img)
        Game.draw_text(self.screen,
                       "Velocity = {}".format(self.player.velocity),
                       size=20,
                       pos=(100, 50))
        Game.draw_text(self.screen,
                       "Position= {}".format(self.player.position),
                       size=20,
                       pos=(100, 100))
        self._detect_collisions()

    def _detect_collisions(self):
        self._laser_with_mobs_collision()
        self._player_with_powerup_collision()
        self._player_with_mobs_collision()

    def _laser_with_mobs_collision(self):
        hits = pg.sprite.groupcollide(
            groupa=self.mobs,
            groupb=self.bullets,
            dokilla=True,
            dokillb=True
        )
        for hit in hits:
            self.score += hit.radius
            # show and play explosion
            self.explosion = Explosion(self, hit.rect.center, size='large')
            self.all_sprites.add(self.explosion)
            self.mobs_explode_snd.play()
            self._respawn_mob()
            # randomly yield bonus under the collided mob
            if random.random() > BONUS_ODD:
                shield = PowerUp(self, hit.rect.center)
                self.powerups.add(shield)
                self.all_sprites.add(shield)

    def _player_with_powerup_collision(self):
        hits = pg.sprite.spritecollide(
            sprite=self.player,
            group=self.powerups,
            dokill=True
        )
        for hit in hits:
            if hit.type == 'shield':
                self.player.shield += random.randrange(10, 30)
                self.powerup_shield_snd.play()
                if self.player.shield > SHIELD_MAX:
                    self.player.shield = SHIELD_MAX
            if hit.type == 'bolt_silver':
                self.player.shot_delay -= 150
                self.powerup_laser_snd.play()
                if self.player.shot_delay < SHOTDELAY_MAX:
                    self.player.shot_delay = SHOTDELAY_MAX
            if hit.type == 'bolt_gold':
                self.powerup_laser_snd.play()
                self.player.powerup()

    def _player_with_mobs_collision(self):
        hits = pg.sprite.spritecollide(
            sprite=self.player,
            group=self.mobs,
            dokill=True,
            collided=pg.sprite.collide_circle
        )
        for hit in hits:
            self.player.shield -= hit.radius * 2

            if self.player.shield <= 0:
                # explode the ship !
                position = self.player.rect.center
                self.explosion = Explosion(self, position, size='player')
                self.all_sprites.add(self.explosion)
                self.ship_explode_snd.play()
                self.player.lives -= 1
                self.player.power_level = 1
                self.player.shield = SHIELD_MAX
                self.player.shot_delay = SHOTDELAY_INIT
                self.player.hide()
            else:
                # show and play explosion
                explosion = Explosion(self, hit.rect.center)
                self.all_sprites.add(explosion)
                self.mobs_explode_snd.play()
                self._respawn_mob()

    def events(self):
        ''' manage events/interactions with users '''
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.playing = self.running = False
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    self.player.shoot()

    def update(self):
        ''' update screen after drawing and checking events '''
        self.all_sprites.update()
        pg.display.flip()

    def show_title(self):
        self.screen.fill((0, 0, 0))
        Game.draw_text(self.screen, "PySpaceX", 80, (WIDTH / 2, HEIGHT / 2))
        Game.draw_text(self.screen,
                       "Press any key to continue...",
                       20,
                       (WIDTH / 2, HEIGHT / 2 + 80))
        pg.display.flip()
        self._wait_kepress()

    def show_gameover(self):
        # don't bother showing gameover if wants to close the window
        if not self.running:
            return
        self.screen.fill((0, 0, 0))
        self.starfield.draw_stars(self.screen)
        Game.draw_text(self.screen, "Game Over", 80, (WIDTH / 2, HEIGHT / 2))
        Game.draw_text(self.screen,
                       "Score = {}".format(self.score),
                       size=40,
                       pos=(WIDTH / 2, HEIGHT / 2 + 80))
        Game.draw_text(self.screen,
                       "Press any key to continue",
                       20,
                       (WIDTH / 2, HEIGHT - 20))
        pg.display.flip()
        self._wait_kepress()

    def _wait_kepress(self):
        waiting = True
        while waiting:
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    waiting = False
                    self.running = False
                elif event.type == pg.KEYDOWN:
                    waiting = False

    def _load_gfx(self):
        ''' Load all game graphics '''
        self.background = pg.image.load(
            os.path.join(IMG_PATH, 'Background/space_background.png')
        ).convert()
        self.background_rect = self.background.get_rect()

        self.player_img = pg.image.load(
            os.path.join(IMG_PATH, 'Ships/playerShip1_blue.png')
        ).convert()

        self.player_mini_img = pg.transform.scale(self.player_img, (25, 19))
        self.player_mini_img.set_colorkey(rgb('black'))

        self.laser_img = pg.image.load(
            os.path.join(IMG_PATH, 'Lasers/laserRed16.png')
        ).convert()

        self.meteors_img = []
        for meteor_path in glob.glob(os.path.join(IMG_PATH, "Meteors/*.png")):
            self.meteors_img.append(pg.image.load(meteor_path).convert())

        self.powerups_img = {}
        self.powerups_img['shield'] = pg.image.load(
            os.path.join(IMG_PATH, 'Powerups/shield_gold.png')
        ).convert()
        self.powerups_img['bolt_silver'] = pg.image.load(
            os.path.join(IMG_PATH, 'Powerups/bolt_silver.png')
        ).convert()
        self.powerups_img['bolt_gold'] = pg.image.load(
            os.path.join(IMG_PATH, 'Powerups/bolt_gold.png')
        ).convert()

        self.explosions_anim = {}
        self.explosions_anim['large'] = []
        self.explosions_anim['small'] = []
        self.explosions_anim['player'] = []
        for _ in range(8):
            img_path = os.path.join(
                EXPLOD_PATH,
                "regularExplosion0{}.png".format(_)
            )
            img = pg.image.load(img_path).convert()
            img.set_colorkey(rgb('black'))
            self.explosions_anim['large'].append(
                pg.transform.scale(img, (75, 75))
            )

            self.explosions_anim['small'].append(
                pg.transform.scale(img, (32, 32))
            )
            img_path = os.path.join(
                EXPLOD_PATH,
                "sonicExplosion0{}.png".format(_)
            )
            img = pg.image.load(img_path).convert()
            img.set_colorkey(rgb('black'))
            self.explosions_anim['player'].append(img)

    def _load_snd(self):
        # Load all sounds
        self.laser_snd = pg.mixer.Sound(
            os.path.join(SND_PATH, 'laser.wav')
        )
        self.mobs_explode_snd = pg.mixer.Sound(
            os.path.join(SND_PATH, 'mobs_explode.wav')
        )
        self.ship_explode_snd = pg.mixer.Sound(
            os.path.join(SND_PATH, 'ship_explode.wav')
        )
        self.powerup_laser_snd = pg.mixer.Sound(
            os.path.join(SND_PATH, 'powerup_laser.wav')
        )
        self.powerup_shield_snd = pg.mixer.Sound(
            os.path.join(SND_PATH, 'powerup_shield.wav')
        )
        # Load background music
        pg.mixer.music.load(
            os.path.join(SND_PATH, 'background.ogg')
        )
        pg.mixer.music.set_volume(0.4)

    def _respawn_mob(self):
        ''' Create a mob sprite and add it to all_sprites and mobs
        sprites group'''
        m = Mob(self)
        self.all_sprites.add(m)
        self.mobs.add(m)

    @staticmethod
    def draw_text(surface, text, size, pos):
        # font = pg.font.Font(FONT_NAME, size)
        font = pg.font.Font(None, size)
        text_surface = font.render(text, True, rgb('white'))
        text_rect = text_surface.get_rect()
        x, y = pos
        text_rect.midtop = (x, y)
        surface.blit(text_surface, text_rect)

    @staticmethod
    def draw_shield_bar(surface, x, y, shield_value):
        if shield_value <= 0:
            shield_value = 0

        BAR_LENGTH = 100
        BAR_HEIGHT = 10
        PADDING = 2
        fill_length = (shield_value * BAR_LENGTH) / 100
        outline_rect = pg.Rect(x, y, BAR_LENGTH, BAR_HEIGHT)
        inline_rect = pg.Rect(
            x + PADDING,
            y + PADDING,
            fill_length - PADDING,
            BAR_HEIGHT - 3
        )
        pg.draw.rect(surface, rgb('white'), outline_rect, PADDING)
        pg.draw.rect(surface, rgb('green'), inline_rect)

    @staticmethod
    def draw_lives(surface, x, y, lives, img):
        for i in range(lives):
            img_rect = img.get_rect()
            img_rect.x = x - 30 * i
            img_rect.y = y
            surface.blit(img, img_rect)

if __name__ == "__main__":
    spacex = Game()
    spacex.show_title()

    while spacex.running:
        spacex.new()
        spacex.show_gameover()

    pg.quit()
    sys.exit(0)


import os
from sqlalchemy import String, create_engine
from sqlalchemy import Table, Column, Integer
from sqlalchemy.dialects.postgresql import OID
from sqlalchemy.ext.declarative import declarative_base

url = (
    f"postgresql+psycopg2://{os.getenv('POSTGRES_USER')}:{os.getenv('POSTGRES_PASSWORD')}@"
    f"localhost:5432/{os.getenv('POSTGRES_DB')}"
)
Base = declarative_base()
engine = create_engine(url, echo=True)
Table(
    "attachment",
    Base.metadata,
    Column("id", Integer, primary_key=True),
    Column("oid", OID),
    Column("category", String(255), unique=True, default="Строки"),
    Column("content_type", String(255), nullable=True),
)
Base.metadata.create_all(engine, checkfirst=True)


import requests

image = '/home/mayur/Desktop/Model_Deployment/Deploying Using Sanic/sample_image.jpg'

resp = requests.post("http://0.0.0.0:8000/inference",
                     files={"file": open(image,'rb')})

# extracting the response
print("{}".format(resp.text))


from __future__ import absolute_import
import collections
import copy
import inspect
from uuid import uuid4

import gast
import six

from tangent import annotate
from tangent import annotations as anno
from tangent import ast as ast_
from tangent import cfg
from tangent import comments
from tangent import create
from tangent import errors
from tangent import fixes
from tangent import funcsigs
from tangent import grads
from tangent import naming
from tangent import non_differentiable
from tangent import quoting
from tangent import template
from tangent import tracing
from tangent import utils

# Some AST nodes to fill in to templates that use stacks or reset gradients
PUSH = quoting.quote('tangent.push')
POP = quoting.quote('tangent.pop')
anno.setanno(PUSH, 'push_func', True)
anno.setanno(POP, 'pop_func', True)
PUSH_STACK = quoting.quote('tangent.push_stack')
POP_STACK = quoting.quote('tangent.pop_stack')
anno.setanno(PUSH_STACK, 'push_func', True)
anno.setanno(POP_STACK, 'pop_func', True)

def _generate_op_id():
  return quoting.quote("'_{}'".format(uuid4().hex[:8]))

def get_push_pop():
  """Create pop and push nodes that are linked.

  Returns:
    A push and pop node which have `push_func` and `pop_func` annotations
        respectively, identifying them as such. They also have a `pop` and
        `push` annotation respectively, which links the push node to the pop
        node and vice versa.
  """
  push = copy.deepcopy(PUSH)
  pop = copy.deepcopy(POP)
  anno.setanno(push, 'pop', pop)
  anno.setanno(push, 'gen_push', True)
  anno.setanno(pop, 'push', push)
  op_id = _generate_op_id()
  return push, pop, op_id

def get_push_pop_stack():
  """Create pop and push nodes for substacks that are linked.

  Returns:
    A push and pop node which have `push_func` and `pop_func` annotations
        respectively, identifying them as such. They also have a `pop` and
        `push` annotation respectively, which links the push node to the pop
        node and vice versa.
  """
  push = copy.deepcopy(PUSH_STACK)
  pop = copy.deepcopy(POP_STACK)
  anno.setanno(push, 'pop', pop)
  anno.setanno(push, 'gen_push', True)
  anno.setanno(pop, 'push', push)
  op_id = _generate_op_id()
  return push, pop, op_id

class ReverseAD(object):
  """Generate a primal and adjoint for a given AST tree.

  This class walks the AST recursively and for each node returns a new primal
  and an adjoint. Note that it relies on function calls being resolved by the
  `resolve_calls` function.

  Each created node is annoted with its corresponding primal or adjoint in
  the `pri` and `adj` annotations respectively.

  Args:
    wrt: A tuple of argument indices with respect to which the gradient should
        be taken.
    preserve_result: A boolean indicating whether or not the generated gradient
        function should also return the output of the original function.
    check_dims: A boolean indicating whether the seed derivatives should have
        their dimensions checked to match their primal counterpart.

  Attributes:
    required: List of user-defined functions that the primal calls.
        The primals and adjoints of this function need to be available in the
        global namespace.
  """

  def __init__(self, wrt, preserve_result, check_dims):
    self.required = []
    self.wrt = wrt
    self.preserve_result = preserve_result
    self.check_dims = check_dims

  def visit(self, node):
    """Visit a node.

    This method is largely modelled after the ast.NodeTransformer class.

    Args:
      node: The node to visit.

    Returns:
      A tuple of the primal and adjoint, each of which is a node or a list of
      nodes.
    """
    method = 'visit_' + node.__class__.__name__
    if not hasattr(self, method):
      raise ValueError('Unknown node type: %s' % node.__class__.__name__)
    visitor = getattr(self, method)

    # If this node is a statement, inform all child nodes what the active
    # variables in this statement are
    if anno.hasanno(node, 'active_in'):
      self.active_variables = anno.getanno(node, 'active_in')
    pri, adj = visitor(node)

    # Annotate primal and adjoint statements
    if isinstance(pri, gast.AST):
      anno.setdefaultanno(pri, 'adj', adj)
    else:
      for node in pri:
        anno.setdefaultanno(node, 'adj', adj)
    if isinstance(adj, gast.AST):
      anno.setdefaultanno(adj, 'pri', pri)
    else:
      for node in adj:
        anno.setdefaultanno(node, 'pri', pri)

    return pri, adj

  @property
  def stack(self):
    if not hasattr(self, '_stack'):
      self._stack = quoting.quote(self.namer.unique(naming.STACK_NAME))
    return ast_.copy_node(self._stack)

  @property
  def substack(self):
    if not hasattr(self, '_substack'):
      self._substack = quoting.quote(self.namer.unique(naming.SUBSTACK_NAME))
    return ast_.copy_node(self._substack)

  def is_active(self, node):
    """Checks whether a statement is active.

    An assignment is active when its right hand side contains active
    variables.

    Args:
      node: an instance of gast.Assign

    Returns:
      Whether the statement is active.
    """
    # Special case: If the right hand side is a pop statement, we want to
    # process it
    if (isinstance(node.value, gast.Call) and
        anno.getanno(node.value, 'func', False) == utils.pop):
      return True
    for succ in gast.walk(node.value):
      if (isinstance(succ, gast.Name) and isinstance(succ.ctx, gast.Load) and
          succ.id in self.active_variables):
        return True
    return False

  def visit_FunctionDef(self, node):
    # Construct a namer to guarantee we create unique names that don't
    # override existing names
    self.namer = naming.Namer.build(node)

    # Check that this function has exactly one return statement at the end
    return_nodes = [n for n in gast.walk(node) if isinstance(n, gast.Return)]
    if ((len(return_nodes) > 1) or not isinstance(node.body[-1], gast.Return)):
      raise ValueError('function must have exactly one return statement')
    return_node = ast_.copy_node(return_nodes[0])

    # Perform AD on the function body
    body, adjoint_body = self.visit_statements(node.body[:-1])

    # Annotate the first statement of the primal and adjoint as such
    if body:
      body[0] = comments.add_comment(body[0], 'Beginning of forward pass')
    if adjoint_body:
      adjoint_body[0] = comments.add_comment(
          adjoint_body[0], 'Beginning of backward pass')

    # Before updating the primal arguments, extract the arguments we want
    # to differentiate with respect to
    dx = gast.Tuple([create.create_grad(node.args.args[i], self.namer)
                     for i in self.wrt], ctx=gast.Load())

    if self.preserve_result:
      # Append an extra Assign operation to the primal body
      # that saves the original output value
      stored_result_node = quoting.quote(self.namer.unique('result'))
      assign_stored_result = template.replace(
          'result=orig_result',
          result=stored_result_node,
          orig_result=return_node.value)
      body.append(assign_stored_result)
      dx.elts.append(stored_result_node)

    for _dx in dx.elts:
      _dx.ctx = gast.Load()
    return_dx = gast.Return(value=dx)

    # We add the stack as first argument of the primal
    node.args.args = [self.stack] + node.args.args

    # Rename the function to its primal name
    func = anno.getanno(node, 'func')
    node.name = naming.primal_name(func, self.wrt)

    # The new body is the primal body plus the return statement
    node.body = body + node.body[-1:]

    # Find the cost; the first variable of potentially multiple return values
    # The adjoint will receive a value for the initial gradient of the cost
    y = node.body[-1].value
    if isinstance(y, gast.Tuple):
      y = y.elts[0]
    dy = gast.Name(id=self.namer.grad(y.id), ctx=gast.Param(),
                   annotation=None)

    if self.check_dims:

      def shape_match_template(primal, adjoint):
        assert tangent.shapes_match(
            primal, adjoint
        ), 'Shape mismatch between return value (%s) and seed derivative (%s)' % (
            numpy.shape(primal), numpy.shape(adjoint))

      shape_check = template.replace(shape_match_template, primal=y, adjoint=dy)
      adjoint_body = shape_check + adjoint_body

    # Construct the adjoint
    adjoint_template = grads.adjoints[gast.FunctionDef]
    adjoint, = template.replace(adjoint_template, namer=self.namer,
                                adjoint_body=adjoint_body, return_dx=return_dx)
    adjoint.args.args.extend([self.stack, dy])
    adjoint.args.args.extend(node.args.args[1:])
    adjoint.name = naming.adjoint_name(func, self.wrt)

    return node, adjoint

  def visit_statements(self, nodes):
    """Generate the adjoint of a series of statements."""
    primals, adjoints = [], collections.deque()
    for node in nodes:
      primal, adjoint = self.visit(node)
      if not isinstance(primal, list):
        primal = [primal]
      if not isinstance(adjoint, list):
        adjoint = [adjoint]
      # Methods will return `None` if the node is to be removed, so remove them
      primals.extend(filter(None, primal))
      # We reverse the order of the adjoints, but not the statements in
      # the adjoint itself
      adjoints.extendleft(filter(None, adjoint[::-1]))
    return primals, list(adjoints)

  def visit_For(self, node):
    if node.orelse:
      raise ValueError

    # Construct the primal and adjoint of the loop
    body, adjoint_body = self.visit_statements(node.body)

    # We create a loop counter which will be pushed on the stack
    push, pop, op_id = get_push_pop()
    counter = self.namer.counter()

    # In `for i in range ...` the variable `i` is the target, which we
    # temporarily set aside each iteration to push to the stack later
    push_target, pop_target, op_id_target = get_push_pop()
    tmp_target = create.create_temp(node.target, self.namer)

    primal_template = grads.primals[gast.For]
    primal = template.replace(
        primal_template,
        body=body,
        i=counter,
        push=push,
        target=node.target,
        iter_=node.iter,
        push_target=push_target,
        _target=tmp_target,
        _stack=self.stack,
        op_id_iter=op_id,
        op_id_target=op_id_target)

    adjoint_template = grads.adjoints[gast.For]
    adjoint = template.replace(
        adjoint_template,
        adjoint_body=adjoint_body,
        i=counter,
        pop=pop,
        pop_target=pop_target,
        target=ast_.copy_node(node.target),
        _stack=self.stack,
        op_id_iter=op_id,
        op_id_target=op_id_target)

    return primal, adjoint

  def visit_While(self, node):
    if node.orelse:
      raise ValueError

    body, adjoint_body = self.visit_statements(node.body)

    # We create a loop counter which will be pushed on the stack
    push, pop, op_id = get_push_pop()
    counter = self.namer.counter()

    primal_template = grads.primals[gast.While]
    primal = template.replace(
        primal_template,
        namer=self.namer,
        body=body,
        i=counter,
        push=push,
        test=node.test,
        _stack=self.stack,
        op_id=op_id)

    adjoint_template = grads.adjoints[gast.While]
    adjoint = template.replace(
        adjoint_template,
        namer=self.namer,
        adjoint_body=adjoint_body,
        i=counter,
        pop=pop,
        _stack=self.stack,
        op_id=op_id)

    return primal, adjoint

  def visit_With(self, node):
    """Deal with the special with insert_grad_of(x) statement."""
    if ast_.is_insert_grad_of_statement(node):
      primal = []
      adjoint = node.body
      if isinstance(adjoint[0], gast.With):
        _, adjoint = self.visit(adjoint[0])
      node.body[0] = comments.add_comment(node.body[0], 'Inserted code')
      # Rename the gradients
      replacements = {}
      for item in node.items:
        if (not isinstance(item.context_expr.args[0], gast.Name) or
            not isinstance(item.optional_vars, gast.Name)):
          raise ValueError
        replacements[item.optional_vars.id] = create.create_grad(
            item.context_expr.args[0], self.namer)
      template.ReplaceTransformer(replacements).visit(node)
      return primal, adjoint
    else:
      return node, []

  def visit_If(self, node):
    # Get the primal and adjoint of the blocks
    body, adjoint_body = self.visit_statements(node.body)
    orelse, adjoint_orelse = self.visit_statements(node.orelse)

    # We will store the condition on the stack
    cond = self.namer.cond()
    push, pop, op_id = get_push_pop()

    # Fill in the templates
    primal_template = grads.primals[gast.If]
    primal = template.replace(
        primal_template,
        body=body,
        cond=cond,
        test=node.test,
        orelse=orelse,
        push=push,
        _stack=self.stack,
        op_id=op_id)
    adjoint_template = grads.adjoints[gast.If]
    adjoint = template.replace(
        adjoint_template,
        cond=cond,
        adjoint_body=adjoint_body,
        adjoint_orelse=adjoint_orelse,
        pop=pop,
        _stack=self.stack,
        op_id=op_id)
    return primal, adjoint

  def visit_Attribute(self, node):
    raise ValueError('attributes are not yet supported for gradients')

  def visit_AugAssign(self, node):
    raise ValueError('AugAssign not supported for gradients. '
      'Should have been removed by ANF. This is a bug.')

  def visit_Assign(self, node):
    """Visit assignment statement."""
    if len(node.targets) != 1:
      raise ValueError('no support for chained assignment')

    # Before the node gets modified, get a source code representation
    # to add as a comment later on
    if anno.hasanno(node, 'pre_anf'):
      orig_src = anno.getanno(node, 'pre_anf')
    else:
      orig_src = quoting.unquote(node)

    # Set target for the RHS visitor to access
    self.orig_target = ast_.copy_node(node.targets[0])

    # If we know we're going to be putting another stack on the stack,
    # we should try to make that explicit
    if isinstance(node.value, gast.Call) and \
        anno.hasanno(node.value, 'func') and \
        anno.getanno(node.value, 'func') in (utils.Stack, utils.pop_stack):
      push, pop, op_id = get_push_pop_stack()
    else:
      push, pop, op_id = get_push_pop()
    push_stack, pop_stack, op_id_stack = get_push_pop_stack()

    # Every assignment statement requires us to store the pre-value, and in the
    # adjoint restore the value, and reset the gradient
    store = template.replace(
        'push(_stack, y, op_id)',
        push=push,
        y=self.orig_target,
        _stack=self.stack,
        op_id=op_id)
    create_substack = template.replace(
        'substack = tangent.Stack()', substack=self.substack)
    store_substack = template.replace(
        'push(stack, substack, op_id)',
        push=push_stack,
        stack=self.stack,
        substack=self.substack,
        op_id=op_id_stack)
    restore = template.replace(
        'y = pop(_stack, op_id)',
        _stack=self.stack,
        pop=pop,
        y=ast_.copy_node(self.orig_target),
        op_id=op_id)
    restore_substack = template.replace(
        'substack = pop(stack, op_id)',
        pop=pop_stack,
        stack=self.stack,
        substack=self.substack,
        op_id=op_id_stack)
    reset = template.replace(
        'd[y] = init_grad(y, allow_lazy_initializer=True)',
        y=self.orig_target,
        init_grad=utils.INIT_GRAD,
        namer=self.namer,
        replace_grad=template.Replace.FULL)

    # If there are no active nodes, we don't need to find an adjoint
    # We simply store and restore the state, and reset the gradient
    if not self.is_active(node):
      return [store, node], [restore, reset]

    # We create a temporary variable for the target that the RHS can use
    self.target = create.create_temp(self.orig_target, self.namer)
    create_tmp = template.replace(
        'tmp = y', tmp=self.target, y=self.orig_target)

    # Get the primal and adjoint of the RHS expression
    try:
      fx, adjoint_rhs = self.visit(node.value)
    except ValueError as e:
      context = [t.id if hasattr(t, 'id') else t for t in node.targets]
      raise ValueError(
          'Failed to process assignment to: %s. Error: %s' % (context, e))
    if not isinstance(adjoint_rhs, list):
      adjoint_rhs = [adjoint_rhs]

    # Walk the RHS adjoint AST to find temporary adjoint variables to sum
    accumulations = []
    for n in adjoint_rhs:
      for succ in gast.walk(n):
        if anno.hasanno(succ, 'temp_adjoint_var'):
          xi = anno.getanno(succ, 'temp_adjoint_var')
          dxi_partial = ast_.copy_node(succ)
          accumulations.append(template.replace(
              'd[xi] = add_grad(d[xi], dxi_partial)',
              namer=self.namer, replace_grad=template.Replace.FULL,
              xi=xi, dxi_partial=dxi_partial, add_grad=utils.ADD_GRAD))

    # The primal consists of storing the state and then performing the
    # assignment with the new primal.
    # The primal `fx` may be optionally (but rarely) redefined when the
    # adjoint is generated, in `fx, adjoint_rhs = self.visit(node.value)`.
    # If we see that the primal value is an Assign node, or a list of nodes
    # (with at least one being an Assign) node, we allow the primal to change.
    # Otherwise, we'll build our own Assign node.
    if isinstance(fx, gast.Assign):
      assign = [fx]
    elif (isinstance(fx, list) and
          any([isinstance(ifx, gast.Assign) for ifx in fx])):
      assign = fx
    else:
      assign = template.replace(
          'y = fx', y=ast_.copy_node(self.orig_target), fx=fx)
      assign = [assign]
    primal = [store, create_substack, store_substack] + assign

    # The adjoint involves creating the temporary, restoring the store,
    # calculating the adjoint, resetting the gradient, and finally accumulating
    # the partials
    adjoint = [create_tmp, restore_substack, restore
              ] + adjoint_rhs + [reset] + accumulations

    # If the LHS is a subscript assignment with variable index, we need to
    # store and restore that as well
    if (isinstance(self.orig_target, gast.Subscript) and
        isinstance(self.orig_target.slice.value, gast.Name)):
      push, pop, op_id = get_push_pop()
      i = self.orig_target.slice.value
      push_index = template.replace(
          'push(_stack, i, op_id)',
          push=push,
          i=i,
          _stack=self.stack,
          op_id=op_id)
      pop_index = template.replace(
          'i = pop(_stack, op_id)',
          pop=pop,
          i=i,
          _stack_=self.stack,
          op_id=op_id)

      primal.insert(len(primal), push_index)
      adjoint.insert(0, pop_index)

    # Add a comment in the backwards pass, indicating which
    # lines in the forward pass generated the adjoint
    for i, adj in enumerate(adjoint):
      adjoint[i] = comments.add_comment(adj, 'Grad of: %s' % orig_src)

    return primal, adjoint

  def visit_Pass(self, node):
    return node, []

  def visit_Subscript(self, node):
    adjoint = template.replace('d[x[i]] = d[y]', namer=self.namer,
                               y=self.target, x=node.value,
                               i=node.slice.value)
    return node, adjoint

  def visit_Name(self, node):
    adjoint = template.replace('d[x] = tangent.copy(d[y])',
                               namer=self.namer, x=node, y=self.target)
    return node, adjoint

  def visit_Num(self, node):
    return node, []

  def visit_Tuple(self, node):
    return self.visit_container(node)

  def visit_List(self, node):
    return self.visit_container(node)

  def visit_container(self, node):
    adjoint = []
    for i, elt in enumerate(node.elts):
      adjoint.append(template.replace('d[x] = d[t[i]]', namer=self.namer,
                                      t=self.target, i=gast.Num(n=i), x=elt))
    return node, adjoint

  def visit_Pass(self, node):
    return node, []

  def visit_BinOp(self, node):
    op = type(node.op)
    if op not in grads.adjoints:
      raise ValueError('unknown binary operator')
    adjoint_template = grads.adjoints[op]
    adjoint = template.replace(adjoint_template,
                               namer=self.namer,
                               x=node.left, y=node.right, z=self.target)
    return node, adjoint

  def visit_UnaryOp(self, node):
    op = type(node.op)
    if op not in grads.adjoints:
      raise ValueError('unknown unary operator')
    adjoint_template = grads.adjoints[op]
    adjoint = template.replace(adjoint_template, namer=self.namer,
                               x=node.operand, y=self.target)
    return node, adjoint

  def visit_Compare(self, node):
    return node, []

  def visit_Assert(self, node):
    return node, []

  def primal_and_adjoint_for_tracing(self, node):
    """Build the primal and adjoint of a traceable function.

    Args:
      node: ast.Call node of a function we wish to trace, instead of transform

    Returns:
      primal: new ast.Assign node to replace the original primal call
      adjoint: new ast.Assign node using the VJP generated in primal to
        calculate the adjoint.
    """
    primal_template = grads.primals[tracing.Traceable]
    adjoint_template = grads.adjoints[tracing.Traceable]

    # Prep
    to_pack = node.args
    target = ast_.copy_node(self.orig_target)
    vjp = quoting.quote(self.namer.unique('%s_grad' % node.func.id))
    tmp = create.create_temp(quoting.quote('tmp'), self.namer)
    assert len(node.keywords) == 0

    # Full replacement of primal
    # TODO: do we need to set 'pri_call' on this?
    primal = template.replace(
        primal_template,
        namer=self.namer,
        result=target,
        fn=node.func,
        tmp=tmp,
        vjp=vjp,
        args=gast.Tuple(elts=to_pack, ctx=gast.Load()))

    # Building adjoint using the vjp generated with the primal
    dto_pack = gast.Tuple(
        elts=[create.create_temp_grad(arg, self.namer) for arg in to_pack],
        ctx=gast.Store())

    adjoint = template.replace(
        adjoint_template,
        namer=self.namer,
        result=target,
        vjp=vjp,
        dargs=dto_pack)

    return primal, adjoint

  def visit_Call(self, node):
    """Create adjoint for call.

    We don't allow unpacking of parameters, so we know that each argument
    gets passed in explicitly, allowing us to create partials for each.
    However, templates might perform parameter unpacking (for cases where
    the number of arguments is variable) and express their gradient as a
    tuple. In this case, we have to unpack this tuple of partials.
    """
    # Find the function we are differentiating
    func = anno.getanno(node, 'func')

    if func in non_differentiable.NON_DIFFERENTIABLE:
      return node, []

    if func == tracing.Traceable:
      return self.primal_and_adjoint_for_tracing(node)

    if func in grads.UNIMPLEMENTED_ADJOINTS:
      raise errors.ReverseNotImplementedError(func)

    # If we don't have an adjoint, we will have to step into the called
    # function and differentiate it
    if func not in grads.adjoints:
      active_args = tuple(i for i, arg in enumerate(node.args)
                          if arg.id in self.active_variables)

      already_counted = False
      for f, a in self.required:
        if f.__name__ == func.__name__ and set(a) == set(active_args):
          already_counted = True
          break
      if not already_counted:
        self.required.append((func, active_args))

      pri_name = naming.primal_name(func, active_args)
      pri_call = gast.Call(
          func=gast.Name(id=pri_name, ctx=gast.Load(), annotation=None),
          args=[self.substack] + node.args,
          keywords=node.keywords)
      anno.setanno(pri_call, 'pri_call', True)

      dy = create.create_grad(self.target, self.namer)
      dy.ctx = gast.Load()
      dx = create.create_grad(node.args[0], self.namer)
      dx.ctx = gast.Store()
      adj_name = naming.adjoint_name(func, active_args)
      adj_call = gast.Call(
          func=gast.Name(id=adj_name, ctx=gast.Load(), annotation=None),
          args=[self.substack, dy] + node.args,
          keywords=node.keywords)
      anno.setanno(adj_call, 'adj_call', True)
      adjoint = [template.replace('dxs = dfx', namer=self.namer, dfx=adj_call)]
      for j, i in enumerate(active_args):
        adjoint.append(template.replace('d[x] = dxs[i]', namer=self.namer,
                                        x=node.args[i].id, i=gast.Num(n=j)))
      return pri_call, adjoint

    # We have a template for the gradient that we need to fill in
    template_ = grads.adjoints[func]

    # Match the function call to the template
    sig = funcsigs.signature(template_)
    sig = sig.replace(parameters=list(sig.parameters.values())[1:])
    kwargs = dict((keyword.arg, keyword.value) for keyword in node.keywords)
    bound_args = sig.bind(*node.args, **kwargs)

    # Fill in any missing kwargs with the defaults from the template
    args = quoting.parse_function(template_).body[0].args
    kwargs = dict(zip(*map(reversed, [args.args, args.defaults])))
    kwargs.update(dict(zip(args.kwonlyargs, args.kw_defaults)))
    for arg, val in kwargs.items():
      if arg.id not in bound_args.arguments:
        bound_args.arguments[arg.id] = val

    # Let's fill in the template. The first argument is the output, which
    # was stored in a temporary variable
    output_name = six.get_function_code(template_).co_varnames[0]
    arg_replacements = {output_name: ast_.copy_node(self.target)}
    arg_replacements.update(bound_args.arguments)

    # If the template uses *args, then we pack the corresponding inputs
    packing = []
    flags = six.get_function_code(template_).co_flags

    if flags & inspect.CO_VARARGS:
      to_pack = node.args[six.get_function_code(template_).co_argcount - 1:]
      vararg_name = six.get_function_code(template_).co_varnames[-1]
      target = gast.Name(annotation=None, id=vararg_name, ctx=gast.Store())
      value = gast.Tuple(elts=to_pack, ctx=gast.Load())
      packing = [gast.Assign(targets=[target], value=value)]

      # And we fill in the packed tuple into the template
      arg_replacements[six.get_function_code(
          template_).co_varnames[-1]] = target
    adjoint = template.replace(template_, namer=self.namer, **arg_replacements)
    unpacking = []
    if flags & inspect.CO_VARARGS:
      # If the template packs arguments, then we have to unpack the
      # derivatives afterwards
      # We also have to update the replacements tuple then
      dto_pack = [create.create_temp_grad(arg, self.namer)
                  for arg in to_pack]
      value = create.create_grad(target, self.namer)
      target = gast.Tuple(elts=dto_pack, ctx=gast.Store())
      unpacking = [gast.Assign(targets=[target], value=value)]

    return node, packing + adjoint + unpacking

  def visit_Expr(self, node):
    # We need to special-case pushes, e.g. utils.push(_stack,x,op_id)
    adjoint = []
    if (isinstance(node.value, gast.Call) and
        anno.getanno(node.value, 'func') == utils.push):
      orig_src = quoting.unquote(node)
      stack, val, op_id = node.value.args
      push_template = grads.adjoints[utils.push]
      adjoint_rhs = template.replace(
          push_template, namer=self.namer, stack=stack, val=val, op_id=op_id)

      # Walk the RHS adjoint AST to find temporary adjoint variables to
      # sum
      accumulation = template.replace(
          'd[xi] = add_grad(d[xi], dxi_partial)', namer=self.namer,
          replace_grad=template.Replace.FULL, xi=val,
          dxi_partial=ast_.copy_node(adjoint_rhs[0].targets[0]),
          add_grad=utils.ADD_GRAD)
      adjoint = adjoint_rhs + [accumulation]
      for i, adj in enumerate(adjoint):
        adjoint[i] = comments.add_comment(adj, 'Grad of: %s' % orig_src)
    return node, adjoint

def reverse_ad(node, wrt, preserve_result, check_dims):
  """Perform reverse-mode AD on an AST.

  This function analyses the AST to determine which variables are active and
  proceeds by taking the naive derivative. Before returning the primal and
  adjoint it annotates push and pop statements as such.

  Args:
    node: A `FunctionDef` AST node.
    wrt: A tuple of argument indices with respect to which we take the
        derivative.
    preserve_result: A boolean indicating whether the generated
        derivative function should also return the original return value.
    check_dims: A boolean indicating whether the seed derivatives should have
        their dimensions checked to match their primal counterpart.

  Returns:
    mod: A `Module` node containing the naive primal and adjoint of the
        function which can be fed to the `split` and `joint` functions.
    required: A list of tuples of functions and argument indices. These
        functions were called by the function but did not have an adjoint.
  """
  if not isinstance(node, gast.FunctionDef):
    raise TypeError
  # Activity analysis
  cfg.forward(node, cfg.Active(wrt))

  ad = ReverseAD(wrt, preserve_result, check_dims)
  pri, adj = ad.visit(node)
  mod = gast.Module(body=[pri, adj])
  mod = annotate.find_stacks(mod)
  return mod, ad.required, ad.stack

def store_state(node, reaching, defined, stack):
  """Push the final state of the primal onto the stack for the adjoint.

  Python's scoping rules make it possible for variables to not be defined in
  certain blocks based on the control flow path taken at runtime. In order to
  make sure we don't try to push non-existing variables onto the stack, we
  defined these variables explicitly (by assigning `None` to them) at the
  beginning of the function.

  All the variables that reach the return statement are pushed onto the
  stack, and in the adjoint they are popped off in reverse order.

  Args:
    node: A module with the primal and adjoint function definitions as returned
        by `reverse_ad`.
    reaching: The variable definitions that reach the end of the primal.
    defined: The variables defined at the end of the primal.
    stack: The stack node to use for storing and restoring state.

  Returns:
    node: A node with the requisite pushes and pops added to make sure that
        state is transferred between primal and adjoint split motion calls.
  """
  defs = [def_ for def_ in reaching if not isinstance(def_[1], gast.arguments)]
  if not len(defs):
    return node
  reaching, original_defs = zip(*defs)

  # Explicitly define variables that might or might not be in scope at the end
  assignments = []
  for id_ in set(reaching) - defined:
    assignments.append(quoting.quote('{} = None'.format(id_)))

  # Store variables at the end of the function and restore them
  store = []
  load = []
  for id_, def_ in zip(reaching, original_defs):
    # If the original definition of a value that we need to store
    # was an initialization as a stack, then we should be using `push_stack`
    # to store its state, and `pop_stack` to restore it. This allows
    # us to avoid doing any `add_grad` calls on the stack, which result
    # in type errors in unoptimized mode (they are usually elided
    # after calling `dead_code_elimination`).
    if isinstance(
        def_, gast.Assign) and 'tangent.Stack()' in quoting.unquote(def_.value):
      push, pop, op_id = get_push_pop_stack()
    else:
      push, pop, op_id = get_push_pop()
    store.append(
        template.replace(
            'push(_stack, val, op_id)',
            push=push,
            val=id_,
            _stack=stack,
            op_id=op_id))
    load.append(
        template.replace(
            'val = pop(_stack, op_id)',
            pop=pop,
            val=id_,
            _stack=stack,
            op_id=op_id))

  body, return_ = node.body[0].body[:-1], node.body[0].body[-1]
  node.body[0].body = assignments + body + store + [return_]
  node.body[1].body = load[::-1] + node.body[1].body

  return node

def split(node, stack):
  """Carry over the state from the primal to the adjoint.

  Args:
    node: A module with the primal and adjoint function definitions as returned
        by `reverse_ad`.
    stack: The stack node to use for storing and restoring state.

  Returns:
    func: A `Module` node with two function definitions containing the primal
        and adjoint respectively.
  """
  node, defined, reaching = _fix(node)

  # Store and restore the state
  node = store_state(node, reaching, defined, stack)

  # Clean up
  anno.clearanno(node)
  return node

def joint(node):
  """Merge the bodies of primal and adjoint into a single function.

  Args:
    node: A module with the primal and adjoint function definitions as returned
        by `reverse_ad`.

  Returns:
    func: A `Module` node with a single function definition containing the
        combined primal and adjoint.
  """
  node, _, _ = _fix(node)
  body = node.body[0].body[:-1] + node.body[1].body
  func = gast.Module(body=[gast.FunctionDef(
      name=node.body[0].name, args=node.body[1].args, body=body,
      decorator_list=[], returns=None)])
  # Clean up
  anno.clearanno(func)
  return func

def _fix(node):
  """Fix the naive construction of the adjont.

  See `fixes.py` for details.

  This function also returns the result of reaching definitions analysis so
  that `split` mode can use this to carry over the state from primal to
  adjoint.

  Args:
    node: A module with the primal and adjoint function definitions as returned
        by `reverse_ad`.

  Returns:
    node: A module with the primal and adjoint function with additional
        variable definitions and such added so that pushes onto the stack and
        gradient accumulations are all valid.
    defined: The variables defined at the end of the primal.
    reaching: The variable definitions that reach the end of the primal.
  """
  # Do reaching definitions analysis on primal and adjoint
  pri_cfg = cfg.CFG.build_cfg(node.body[0])
  defined = cfg.Defined()
  defined.visit(pri_cfg.entry)
  reaching = cfg.ReachingDefinitions()
  reaching.visit(pri_cfg.entry)

  cfg.forward(node.body[1], cfg.Defined())
  cfg.forward(node.body[1], cfg.ReachingDefinitions())

  # Remove pushes of variables that were never defined
  fixes.CleanStack().visit(node)
  fixes.FixStack().visit(node.body[0])

  # Change accumulation into definition if possible
  fixes.CleanGrad().visit(node.body[1])
  # Define gradients that might or might not be defined
  fixes.FixGrad().visit(node.body[1])
  return node, defined.exit, reaching.exit