import math

import gmpy2

import parsenumber
import utils

def brute(s, g, mp):
    end = s + g
    composite = [n % 2 == 0 for n in range(s, end + 1)]

    if s <= 1 <= end:
        # 1 is composite
        composite[1 - s] = True

    if s <= 2 <= end:
        # 2 is prime
        composite[2 - s] = False

    last = min(int(math.sqrt(end)) + 1, mp)
    assert last == mp or last * last >= end

    # check if odds have small factors
    for p in range(3, mp+1, 2):
        # mark off all factors p*p + i*p
        p2 = p * p
        if p2 >= s:
            start = p2 - s
        else:
            # First odd multiple > s
            div, start = divmod(-s, p)
            if div % 2 == 0:
                start += p

        for m in range(start, g+1, 2*p):
            composite[m] = True

    return composite

def test_sieve():
    for s, g, mp in (
        (1, 100, 10),
        (11, 89, 10),
        (100, 100, 10),
        (1001, 100, 10),
        (1001, 1000, 10),
        (1001, 100, 50),
        (1001, 1000, 50),
        (5, 1000, 3),
   ):
        expect = brute(s, g, mp)
        result = utils.sieve(s, g, mp)
        assert len(expect) == len(result)
        #print([1 * v for v in expect])
        #print([1 * v for v in result])
        #print([s + i for i, v in enumerate(expect) if v])
        print([(s+i, a, b) for i, (a, b) in enumerate(zip(expect, result)) if a != b])
        assert expect == result, (s, g, mp)

def test_sieve_primepi():
    # Easy to generate these with `primesieve <start> <start + gap>
    for s, g, mp, expected in (
        (0,  100, 10, 25),
        (1,   99, 10, 25),
        (1,   99, 100, 25),
        (101, 100, 20, 21),
        (101, 100, 100, 21),
        (1001, 1000, 3, 334),
        (1001, 1000, 30, 151),
        (1001, 1000, 50, 135),
        (1000001, 10000, 1100, 753),
    ):
        assert expected == utils.sieve(s, g, mp).count(False)

def test_max_prime_overlaps_interval():
    # Test when max_prime > N
    for s, g, mp, expected in (
        (1, 99, 2, 50),
        (1, 99, 3, 34),
        (1, 99, 5, 28),
        (1, 99, 7, 25),
        (1, 99, 11, 25),
        (1, 99, 100, 25),
        (1, 99, 200, 25),
    ):
        #print (s, g, mp, expected, [s+i for i, v in enumerate(utils.sieve(s, g, mp)) if not v])
        assert expected == utils.sieve(s, g, mp).count(False)
        assert utils.sieve(s, g, mp) == brute(s, g, mp)

def test_limit_limitted():
    # Test that limit is decreased to sqrt(s + g)
    mp = 10 ** 10
    for s, g, expected in (
        (1, 99, 25),
        (1000, 1000, 135),
        (10 ** 6, 100, 6),
        (10 ** 9, 100, 7),
    ):
        composites = utils.sieve(s, g, mp)
        for i, c in enumerate(composites):
            assert gmpy2.is_prime(s + i) == (not c)
        assert expected == composites.count(False)

def test_zero_one_composite():
    # Test that 0, 1 are "composite" in our sieve
    for s, g in (
        (0, 10),
        (1, 5),
        (0, 1),
        (0, 2),
        (1, 1),
        (1, 2)
    ):
        composites = utils.sieve(s, g, 10)
        if s == 0:
            assert composites[0] == True
        if s + g - 1 >= 1:
            assert composites[1 - s] == True

# TODO sieve_factor tests

def test_validate():
    for s, g in ((101, 2), (103, 4), (113, 14), (360653, 96),
                 (18361375334787046697, 1550),
                 (9691983639208775401081992556968666567067, 2982)):
        assert utils.validate(s, g), (s, g)
        assert utils.validate(str(s), g), (s, g)
        if g > 2:
            assert not utils.validate(s, g - 2)
        assert not utils.validate(s, g + 2)

def test_validate_string():
    assert utils.validate("11051077202945*97#/30 -1754", 2900)
    assert utils.validate(1009, 4)
    assert not utils.validate([1009], 4)

def test_check_pfgw_available():
    assert utils.check_pfgw_available(), "Assumed to be true on dev machines"

NUM_STATUS = (
        ("31# +1", True),
        ("379# +1", True),
        ("1019# +1", True),
        ("2657# +1", True),
        ("503# -617", False),
        ("503# -631", False),
        ("503# -659", True)
)

def test_is_prime_large():
    for str_num, result in NUM_STATUS:
        num = parsenumber.parse(str_num)
        assert utils.is_prime_large(num) == result
        assert utils.is_prime_large(num, str_num) == result

def test__is_prime_pfgw():
    for str_num, result in NUM_STATUS:
        num = parsenumber.parse(str_num)
        assert utils._is_prime_pfgw(num) == result
        assert utils._is_prime_pfgw(str_num) == result


from rest_framework.response import Response
from rest_framework import status

# models
from .....models import BloombergFirm

# serializers
from .....serializers.bloomberg_firm import BloombergFirmSerializer

# decorators
from rest_framework.decorators import api_view

# Authentication and Users
from rest_framework.decorators import authentication_classes
from rest_framework import authentication
from .....permissions.permission_decorator import user_has_permissions

@api_view(["GET", "PATCH", "DELETE"])
@authentication_classes([authentication.TokenAuthentication])
@user_has_permissions(modelnames = ["bloombergfirm"])
def bloomberg_firm(request, firm_nr_id):
    # find object in database
    try:
        firm_nr = BloombergFirm.objects.get(pk=firm_nr_id)
    except BloombergFirm.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get firm number by id with all related buildings
    if request.method == "GET":
        depth = int(request.query_params.get('depth', 0))
        serializer = BloombergFirmSerializer(
            firm_nr, context={'depth': depth})
        return Response(serializer.data)

    # update one location by id
    if request.method == "PATCH":
        serializer = BloombergFirmSerializer(
            firm_nr, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


from api.request import OrderComplete
from db.database import DBSession
from db.models import DBOrder
from db.exception import DBDoesntExistsException

def complete_order_query(session: DBSession, rdata: OrderComplete):
    assign_ids: list[int] = session.query(DBOrder.assign_ids).filter((DBOrder.courier_id == rdata.courier_id)
                                                                     & (DBOrder.order_id == rdata.order_id)
                                                                     & (DBOrder.complete_time == None)).first()

    if assign_ids:
        session.query(DBOrder).filter(DBOrder.order_id.in_(assign_ids.assign_ids)
                                      & (DBOrder.order_id != rdata.order_id)
                                      & (DBOrder.complete_time == None))\
            .update({"assign_time": rdata.complete_time}, synchronize_session=False)

        session.query(DBOrder).filter(DBOrder.order_id == rdata.order_id)\
            .update({"complete_time": rdata.complete_time}, synchronize_session=False)
    else:
        raise DBDoesntExistsException


hikvision_url_default = {
    'auth': ('admin', 'admin123'),
    'device_url': 'http://172.22.61.80'}

urls = {
    'brightness':   '/Image/channels/1/Color',
    'framerate':    '/Streaming/channels/1',
    'quality':      '/Streaming/channels/1',
    'height':       '/Streaming/channels/1',
    'width':        '/Streaming/channels/1',
    'exposure':     '/Image/channels/1/shutter',
    'irlight':      '/Image/channels/1/IrcutFilter'
}

place = {
    'brightness':   ['Color', 'brightnessLevel'],
    'framerate':    ['StreamingChannel', 'Video', 'maxFrameRate'],
    'quality':      ['StreamingChannel', 'Video', 'fixedQuality'],
    'height':       ['StreamingChannel', 'Video', 'videoResolutionHeight'],
    'width':        ['StreamingChannel', 'Video', 'videoResolutionWidth'],
    'exposure':     ['Shutter', "ShutterLevel"],
    'irlight':      ['IrcutFilter', 'IrcutFilterType']
}


import pyttsx3
import speech_recognition as sr
import datetime
import wikipedia
import webbrowser
import os
from time import ctime

print('initializing Jarvis')

MASTER = 'Xander'
engine = pyttsx3.init('sapi5')
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[0].id)

def speak(text):
	engine.say(text)
	engine.runAndWait()

def wishMe():
	hour = int(datetime.datetime.now().hour)

	if hour >= 0 and hour < 12:
		speak('Good Morning master..' + MASTER)

	elif hour >= 12 and hour < 18:
		speak('Good Afternoon master...' + MASTER)

	else:
		speak('Good Evening master...' + MASTER)
speak('initializing Jarvis...')
speak('I am Jarvis. How may i help you?')

def takeCommand ():
	r = sr.Recognizer()
	with sr.Microphone() as source:
		print("listening...")
		audio = r.listen(source)

	try:
		print("Recognizing...")
		query = r.recognize_google(audio, language = 'en-us')
		print(f"you said :{query}\n")

	except Exception as e:
		print("say that again please")
		query = None

	return query.lower()

#main program starts here...
wishMe()
query = takeCommand()

 #logic for executing tasks

if "wikipedia" in query:
 	speak('searching wikipedia...')
 	query = query.replace("wikipedia", "")
 	results = wikipedia.summary(query, sentences = 2)
 	speak(results)

#if the spoken text contains "open youtube", then Jarvis opens www.youtube.com on your default browser.....
elif "open youtube" in query:
	webbrowser.open("www.youtube.com")

#if the spoken text contains "play music", then Jarvis opens play a song on your default music player.....
elif "play music" in query:
	songs_dir = r"C:\Users\user\Music\music"
	songs = os.listdir(songs_dir)
	os.startfile(os.path.join(songs_dir,songs[0]))

#if the spoken text contains "time", then Jarvis speaks the current time.....
elif "time" in query or "date" in query:
	speak(f"master {MASTER} the current date and time is {ctime()}")

#if the spoken text contains "sublime text", then Jarvis opens sublime Text .....
elif "sublime text" in query:
	os.startfile(r"C:\Program Files\Sublime Text 3\sublime_text.exe")


# -*- coding: utf-8 -*-
"""
Test Analysis
"""

# -----------------------------------------------------------------------------
# Set "Library Path" - Social_Pain Repos
lib_path = r'C:/Repos/Social_Pain/libs'
#lib_path = r'/home/kampff/Repos/Dreosti-Lab/Social_Pain/libs'

# Set Library Path
import sys
sys.path.append(lib_path)

# -----------------------------------------------------------------------------
# Set Base Path
base_path = r'S:/WIBR_Dreosti_Lab/Alizee/Behaviour_Heat_Gradient'
#base_path = r'/media/kampff/Elements/Adam_Alizee'

# Import useful libraries
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import scipy.misc as misc
from scipy import stats
import seaborn as sns
import pandas as pd
import pylab as pl

# Import local modules
import SP_Utilities as SPU

# Read folder list file
FolderlistFile = base_path + '/Folderlist_WAY267464_Control.txt'
groups, ages, folderNames, fishStatus = SPU.read_folder_list(FolderlistFile)

# XMs
XMs = []

# Bulk analysis of all folders
for idx,folder in enumerate(folderNames):

    # Get Folder Names
    NS_folder, S_folder, Analysis = SPU.get_folder_names(folder)

    # ---------------------
    # Analyze Tracking for each fish
    for i in range(0,6):
        fish_number = i + 1

        # Extract tracking data (NS)
        tracking_file = NS_folder + r'/tracking' + str(fish_number) +'.npz'
        data = np.load(tracking_file)
        tracking = data['tracking']
        fx_NS = tracking[:,0]
        fy_NS = tracking[:,1]
        bx_NS = tracking[:,2]
        by_NS = tracking[:,3]
        ex_NS = tracking[:,4]
        ey_NS = tracking[:,5]
        area_NS = tracking[:,6]
        ort_NS = tracking[:,7]
        motion_NS = tracking[:,8]

        # Extract tracking data (S)
        tracking_file = S_folder + r'/tracking' + str(fish_number) +'.npz'
        data = np.load(tracking_file)
        tracking = data['tracking']
        fx_S = tracking[:,0]
        fy_S = tracking[:,1]
        bx_S = tracking[:,2]
        by_S = tracking[:,3]
        ex_S = tracking[:,4]
        ey_S = tracking[:,5]
        area_S = tracking[:,6]
        ort_S = tracking[:,7]
        motion_S = tracking[:,8]

        # Filter out bad data
        min_x = 250
        max_x = 1000
#=============================================================================

        if fish_number == 1:
            min_y = 150
            max_y = 250
        if fish_number == 2:
            min_y = 270
            max_y = 370
        if fish_number == 3:
            min_y = 390
            max_y = 490
        if fish_number == 4:
            min_y = 510
            max_y = 610
        if fish_number == 5:
            min_y = 630
            max_y = 730
        if fish_number == 6:
            min_y = 750
            max_y = 850
# =============================================================================

        # Find good tracking (NS)
        num_total_frames_NS = len(fx_NS)
        good_frame_NS = (fx_NS > min_x) * (fx_NS < max_x) * (fy_NS > min_y) * (fy_NS < max_y)
        num_good_frames_NS = np.sum(good_frame_NS)
        lost_frame_NS = num_total_frames_NS-num_good_frames_NS

        # Find good tracking (S)
        num_total_frames_S = len(fx_S)
        good_frame_S = (fx_S > min_x) * (fx_S < max_x) * (fy_S > min_y) * (fy_S < max_y)
        num_good_frames_S = np.sum(good_frame_S)
        lost_frame_S = num_total_frames_S-num_good_frames_S

        # All lost frames
        lost_frame = lost_frame_S + lost_frame_NS

        # plt. figure ()
        # plt.plot(fx_NS[good_frame_NS], fy_NS[good_frame_NS], 'b.', alpha = 0.15)
        # plt.plot(fx_S[good_frame_S], fy_S[good_frame_S], 'm.', alpha = 0.15)
        # plt.title("Fish #{0}- Lost Frames {1}".format(fish_number, lost_frame))

        # for i in range(0,6):
        #     plt.savefig(Analysis + '/tracking_summary'+ str(fish_number) + '.png', dpi=300)

        # Store XMs
        XMs.append([np.mean(fx_NS[good_frame_NS]), np.mean(fx_S[good_frame_S])])

    # Report
    print("Next File: {0}".format(idx))

# Crude calibration: 300 = 28 deg, 800 = 36 deg (900/8) pixels per degree
XM_values = np.array(XMs)/62.5
TTSs = XM_values[:,1] - XM_values[:,0]

# Stats: paired Ttest mean position of each fish in NS vs S
s, pvalue_rel = stats.ttest_rel(XM_values[:,1], XM_values[:,0])
s, pvalue_1samp = stats.ttest_1samp(TTSs, 0)

mean_TTS = np.mean(XM_values[:,1] - XM_values[:,0])
sem_TTS = np.std(XM_values[:,1] - XM_values[:,0])/np.sqrt(len(TTSs)-1)

# Make a Plot using Color Gradient Function
#cm = plt.cm.get_cmap('plasma') #Choose color map

# # Plot histogram
# plt.figure()
# np, bins, patches = plt.hist(TTSs, 15)
# bin_centers = 0.5 * (bins[:-1] + bins[1:])

# # scale values to interval [0,1]
# col = bin_centers - min(bin_centers)
# col /= max(col)

# for c, p in zip(col, patches):
#     plt.setp(p, 'facecolor', cm(c))

# plt.vlines(0, 0, 12.5, 'k',)
# plt.xlabel('Tolerated Temperature Shift (°C)')
# plt.ylabel('Fish Count')
# plt.title('Tolerated Temperature Shift (TTS) During Social Cue Viewing\nMean TTS +/- SEM: {0:0.2f} +/- {1:0.2f}\n(p-value: {2:0.4f})'.format(mean_TTS, sem_TTS, pvalue_rel))
# plt.ylim([0, 12.5])
# plt.xlim([-2,6])
# plt.text(4.5, 7, 'n={0}'.format(len(TTSs)))
# sns.despine()
# plt.tight_layout()

# plt.savefig(base_path + '/Figures/TTS_Histplot.png')
# plt.savefig(base_path + '/Figures/TTS_Histplot.eps')

#Plot unique Color Histogram
plt.vlines(0, 0, 18, 'k')
plt.hist(TTSs, bins=20, color='Steelblue')
plt.xlabel('Tolerated Temperature Shift (°C)')
plt.ylabel('Fish Count')
plt.title('Mean position of Test fish in Social vs Non_Social trials\nMean TTS +/- SEM: {0:0.2f} +/- {1:0.2f}\n(p-value: {2:0.4f})'.format(mean_TTS, sem_TTS, pvalue_rel))
plt.ylim([0, 18])
plt.xlim([-4,8])
plt.text(4.5, 14, 'n={0}'.format(len(TTSs)))
sns.despine()
plt.show()

plt.savefig(base_path + '/Figures/TTS_Histplot.png')
plt.savefig(base_path + '/Figures/TTS_Histplot.eps')

#Make Distribution Plot

plt.figure()
plt.vlines(0, 0, 0.75, 'k',)
sns.distplot(TTSs, bins=15)
plt.xlabel('Tolerated Temperature Shift (°C)')
plt.title('Tolerated Temperature Shift (TTS) During Social Cue Viewing\nMean TTS +/- SEM: {0:0.2f} +/- {1:0.2f}\n(p-value: {2:0.4f})'.format(mean_TTS, sem_TTS, pvalue_rel))
plt.text(4.5, 0.45, 'n={0}'.format(len(TTSs)))
sns.despine()
plt.tight_layout()

plt.savefig(base_path + '/Figures/TTS_Distplot.png')
plt.savefig(base_path + '/Figures/TTS_Distplot.eps')

# #FIN


import random
from stringprep import map_table_b2

#words
word_bank = ["avenue", "axiom", "bagpipes", "banjo", "bikini", "blizzard", "cobwebs", "crypt", "espionage", "fluffy",
"galaxy", "gossip", "hydrogen", "jelly", "jukebox", "keyhole", "queue", "quartz", "quiz", "rhythm",
"sphinx", "stretch", "subway", "transcript", "transplant", "waltz", "wizard", "zipper", "zombie", "zodiac"]

hint_bank = ["This word typically comes after a street name.", "something that is established as self evidently true.",
"Scottish instrument", "kind of like a guitar", "it's a type of swimsuit", "a very cold storm", "this goes with halloween and spiders",
"it's underground", "government spying", "used to describe stuffed animals", "the milkyway is considered this", "[] girl. It's a popular TV show",
"the simplest atom", "the type of fish that stings", "old way of playing music", "has to do with a lock and key",
"another way to say line", "crystal people use for manifesting", "easier version of a test", "patterns in music and poetry",
"it has a woman's head and a lion's body", "you should do this before working out.", "a form of public transportation", "document of all your grades",
"type of surgery that involves moving organs", "a classic ballroom dance", "think Harry Potter", "it's on most jackets", "they eat peoples brains",
"What's your star sign?"]

congrats = ["Congrats!", "You got it!", "Yay! You got it!", "Great Job!", "Awesome, you got it!"]
bad = ["try again", "lol wrong", "incorrect", "That's wrong", "try a different letter"]

#generate random word and hint
def conversions():
    word = random.choice(range(len(word_bank))) #index of the word
    hint = (hint_bank[word]) #hint
    word = (word_bank[word]) #the actual word
    word_split = tuple(word)
    guess = list(word)

    for x in range(len(guess)):
        guess[x] = "-"

    user_guess = "".join(guess)
    counter = len(guess) +2

#game
def game():
    print("Welcome to the game! Guess a letter or the whole word before your tries run out to win.")
    print(f"your hint is: {hint}")

    while user_guess != word and counter != 0 and counter > 0:
        letter = input(f"Your word is: {user_guess} Guess a letter or word! You have {counter} tries left. ")

        while letter.isalpha() != True:
            letter = input("Please enter a letter or word to guess: ")

        letter = letter.lower()
        x = random.choice([0, 5])

        for x in range(len(guess)):
            if word_split[x] == letter:
                guess[x] = letter
                user_guess = "".join(guess)
                print(congrats[x])
            elif letter == word:
                user_guess = word

        counter = counter - 1

    #end of game
    if user_guess == word:
        print("Congrats! you guessed the word.")
    else:
        print(f"Sorry you ran out of attempts. The correct word was {word}.")

conversions()
game()

def test(x):
    mt = x * 9 / 5 +32
    return mt

# hACK hACK


#! /usr/bin/env python

from aoc.utils import load_input

def get_layers(img_data, w, h):
    layer_size = w * h

    return [[int(c) for c in img_data[i : i + layer_size]] for i in range(0, len(img_data), layer_size)]

def merge_layers(layers):
    merged = []

    for i in range(len(layers[0])):
        for l in layers:
            if l[i] in (0, 1):
                merged.append(l[i])
                break
        else:
            merged.append(-1)

    return merged

def render_image(img_data, w, h):
    for y in range(h):
        for x in range(w):
            px = img_data[x + y * w]
            print("X" if px == 1 else " ", end="")

        print()

def main():
    img_data = load_input("d08.txt")
    w, h = 25, 6

    nb0 = 2 ** 64
    layers = get_layers(img_data, 25, 6)
    p1 = 0
    for l in layers:
        c = l.count(0)
        if c < nb0:
            p1 = l.count(1) * l.count(2)
            nb0 = c

    print(f"Part 1: {p1}")

    final_img = merge_layers(layers)
    print("Part 2:")
    print(render_image(final_img, w, h))

if __name__ == "__main__":
    from time import time as ts

    _t = ts()
    main()
    _t = ts() - _t

    print(f"Took {_t:.3f}s")


class Solution(object):

    def isEscapePossible(self, blocked, source, target):
        """
        :type blocked: List[List[int]]
        :type source: List[int]
        :type target: List[int]
        :rtype: bool
        """
        blocked = set([(x[0], x[1]) for x in blocked])
        dir = [(-1, 0), (1, 0), (0, 1), (0, -1)]

        def gen_neighbor(x, y):
            for dx, dy in dir:
                if x + dx < 0 or x + dx > 1e6 or y + dy < 0 or y + dy > 1e6:
                    continue
                nx, ny = x + dx, y + dy
                yield nx, ny

        def is_in_box(x, y, center_x, center_y, length):
            return center_x - length <= x <= center_x + length and \
                center_y - length <= y <= center_y + length

        def is_blocked(start_x, start_y, end_x, end_y):
            seen = set()
            stack = [(start_x, start_y)]
            seen.add((start_x, start_y))
            while stack:
                x, y = stack.pop(-1)
                if not is_in_box(x, y, start_x, start_y, len(blocked)) or \
                        (x == end_x and y == end_y):
                    return True

                for nx, ny in gen_neighbor(x, y):
                    if (nx, ny) in blocked or (nx, ny) in seen: continue
                    seen.add((nx, ny))
                    stack.append((nx, ny))
            return False

        return is_blocked(source[0], source[1], target[0], target[1]) and \
               is_blocked(target[0], target[1], source[0], source[1])

if __name__ == "__main__":
    sol = Solution()
    print(sol.isEscapePossible([[1, 1]], [0, 0], [10, 10]))


import contextlib

import pytest

from draconic import DraconicConfig
from draconic.exceptions import AnnotatedException, WrappedException
from draconic.helpers import OperatorMixin

# noinspection PyProtectedMember
@contextlib.contextmanager
def temp_limits(interpreter: OperatorMixin, **limits):
    old_config = interpreter._config
    interpreter._config = DraconicConfig(**limits)
    yield
    interpreter._config = old_config

@contextlib.contextmanager
def raises(expected_exception, **kwargs):
    """
    since we have custom error handling with WrappedExceptions, we have to wrap pytest.raises()
    to account for situations where the actual expected exception is deep at the root of some expression chain
    """
    if isinstance(expected_exception, tuple):
        inner_expected = (*expected_exception, WrappedException)
    else:
        inner_expected = (expected_exception, WrappedException)

    with pytest.raises(inner_expected, **kwargs) as exc_info:
        yield exc_info

    # if we're here, the exception type is either the expected type or an unknown one of our wrappers

    # the exception type is exactly what we expected
    if issubclass(exc_info.type, expected_exception):
        return
    # the exception we care about is deeply nested or wrapped
    if isinstance(exc_info.value, WrappedException) and expected_exception is not AnnotatedException:
        assert isinstance(exc_info.value.original, expected_exception)


import day8.day8_part1 as puzzle
from typing import List
import unittest

class TestDay8Part1(unittest.TestCase):

    message: str ='''be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce'''

    def test_parse_entries(self):

        # Setup
        # ----
        expected_entries: List[puzzle.Entry] = [{
            'signal_patterns': ['be','cfbegad','cbdgef','fgaecd','cgeb','fdcge','agebfd','fecdb','fabcd','edb'],
            'output_digits': ['fdgacbe','cefdb','cefbgd','gcbe'],
        },{
            'signal_patterns': ['edbfga','begcd','cbg','gc','gcadebf','fbgde','acbgfd','abcde','gfcbed','gfec'],
            'output_digits': ['fcgedb','cgb','dgebacf','gc'],
        },{
            'signal_patterns': ['fgaebd','cg','bdaec','gdafb','agbcfd','gdcbef','bgcad','gfac','gcb','cdgabef'],
            'output_digits': ['cg','cg','fdcagb','cbg'],
        },{
            'signal_patterns': ['fbegcd','cbd','adcefb','dageb','afcb','bc','aefdc','ecdab','fgdeca','fcdbega'],
            'output_digits': ['efabcd','cedba','gadfec','cb'],
        },{
            'signal_patterns': ['aecbfdg','fbg','gf','bafeg','dbefa','fcge','gcbea','fcaegb','dgceab','fcbdga'],
            'output_digits': ['gecf','egdcabf','bgf','bfgea'],
        },{
            'signal_patterns': ['fgeab','ca','afcebg','bdacfeg','cfaedg','gcfdb','baec','bfadeg','bafgc','acf'],
            'output_digits': ['gebdcfa','ecba','ca','fadegcb'],
        },{
            'signal_patterns': ['dbcfg','fgd','bdegcaf','fgec','aegbdf','ecdfab','fbedc','dacgb','gdcebf','gf'],
            'output_digits': ['cefg','dcbef','fcge','gbcadfe'],
        },{
            'signal_patterns': ['bdfegc','cbegaf','gecbf','dfcage','bdacg','ed','bedf','ced','adcbefg','gebcd'],
            'output_digits': ['ed','bcgafe','cdgba','cbgef'],
        },{
            'signal_patterns': ['egadfb','cdbfeg','cegd','fecab','cgb','gbdefca','cg','fgcdab','egfdb','bfceg'],
            'output_digits': ['gbdfcae','bgc','cg','cgb'],
        },{
            'signal_patterns': ['gcafb','gcf','dcaebfg','ecagb','gf','abcdeg','gaef','cafbge','fdbac','fegbdc'],
            'output_digits': ['fgae','cfgab','fg','bagce'],
        }]

        # Execute
        # ----
        entries: List[puzzle.Entry] = puzzle.parse_entries(self.message)

        # Verify
        # ----
        self.assertEqual(entries, expected_entries)

    def test_count_1_4_7_8_output_digits(self):

        # Setup
        # ----
        entries: List[puzzle.Entry] = puzzle.parse_entries(self.message)

        # Execute / Verify
        # ----
        count: int = puzzle.count_1_4_7_8_output_digits(entries)
        self.assertEqual(count, 26)


# Final Project of CS1: Intro to Game Programming F20 Fall 2020
# Name: Daeshaun Morrison
# Import sys library
import sys
import time as t
# Import pygame library
import pygame
import random
from random import randint

# Init it for use
pygame.init()
pygame.mixer.init()

# Set constants
WIDTH=640
HEIGHT=480
CLOCK_TICK = 30
SHIPSPEED = 10
ENEMYSPEED = 5
ENEMYAMOUNT = 5
defaultFont = r"assets\\fonts\\NerkoOne-Regular.ttf"
BOMB_SpeedX = 7
BOMB_SpeedY = 25
BOMB_AMOUNT = 5
SPEEDBOOST = 5
# Some basic colors
BLACK     = (0,0,0)
RED       = (255,0,0)
GREEN     = (0,255,0)
BLUE      = (0,0,255)
YELLOW    = (255,255,0)
MAGENTA   = (255,0,255)
CYAN      = (0,255,255)
WHITE     = (255,255,255)

# Set window size and caption
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Space Defender - Daeshaun Morrison")

class Player(pygame.sprite.Sprite):
    # Initialize the sprite.
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        # Load img in folder
        self.loadImage()

        self.frame = 0
        self.delay = 3
        self.pause = 0

        self.image = self.imgList[0]
        self.rect = self.image.get_rect()
        self.rect.center = (300, 400)
        # Allows ship to move
        self.toggle = True
        # Set a var that allows ship to shoot multi-missiles
        self.multiShot = False
        self.speedBoost = False
        # Set timer for multiShot
        self.multiShotTimer = 0
        # Set timer for speedBoost
        self.speedBoostTimer = 0

    def loadImage(self):
        self.imgList = []
        for i in range(1, 5) :
            self.image = pygame.image.load(f"assets\sprites\spaceship_blue_animation/{i}.png")
            self.image = self.image.convert()
            self.transColor = self.image.get_at((1,1))
            self.image.set_colorkey(self.transColor)
            self.image = pygame.transform.scale( self.image, (80, 80))
            self.imgList.append(self.image)

    def update(self):
        # This updates the frame of each sprite to simulate animation
        self.pause += 1
        if self.pause >= self.delay:
            self.pause = 0
            self.frame += 1
            if self.frame >= len(self.imgList):
                self.frame = 0
            self.image = self.imgList[self.frame]
        # If the player collides with a power, set a timer that only counts up when game is unpaused.
        # When timer reaches a __ seconds, turn it off
        if self.multiShot and self.toggle == True :
            self.multiShotTimer += 1
            if self.multiShotTimer > CLOCK_TICK * 5:
                self.multiShotTimer = 0
                self.multiShot = False

        if self.speedBoost and self.toggle == True:
            self.speedBoostTimer += 1
            if self.speedBoostTimer > CLOCK_TICK * 5:
                self.speedBoostTimer = 0
                self.speedBoost = False

        # Allow the user to move using the arrow keys or awsd keys.
        # pygame.key.get_pressed() returns a
        # list of booleans, one for each key.
        # More than one key can be pressed.
        keys = pygame.key.get_pressed()
        # Arrow keys
        if (keys[pygame.K_RIGHT] or keys[pygame.K_d] ) and self.rect.right < WIDTH and self.toggle == True:
            # If player collect speed boost(powerUp), increase their speed by ___
            if self.speedBoost == True :
                self.rect.centerx += (SHIPSPEED + SPEEDBOOST)
            else :
                self.rect.centerx += SHIPSPEED
        if (keys[pygame.K_LEFT] or keys[pygame.K_a]) and self.rect.left > 0 and self.toggle == True:
            if self.speedBoost == True :
                self.rect.centerx -= (SHIPSPEED + SPEEDBOOST)
            else :
                self.rect.centerx -= SHIPSPEED
        if (keys[pygame.K_UP] or keys[pygame.K_w])and self.rect.top > 0 and self.toggle == True:
            if self.speedBoost == True :
                self.rect.centery -= (SHIPSPEED + SPEEDBOOST)
            else :
                self.rect.centery -= SHIPSPEED
        if (keys[pygame.K_DOWN] or keys[pygame.K_s]) and self.rect.bottom < HEIGHT and self.toggle == True:
            if self.speedBoost == True :
                self.rect.centery += (SHIPSPEED + SPEEDBOOST)
            else :
                self.rect.centery += SHIPSPEED

    def get_pos(self):
        return self.rect.center

class Missile(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        # Get image of the whole spritesheet
        self.imgMaster = pygame.image.load("assets\sprites\Royalty-Free-Game-art-Spaceships-from-Unlucky-Studio.png")
        self.imgMaster.convert()
        misImgSize = (45, 77)
        # Create a surface to draw a section of the spritesheet
        self.image = pygame.Surface(misImgSize)
        self.image.blit(self.imgMaster, (0,0), ( (71, 1956) ,(misImgSize)) )
        self.image.set_colorkey( self.image.get_at((1,1)))
        self.image = pygame.transform.scale( self.image, (15, 35) )
        # Get rect of sprite
        self.rect = self.image.get_rect()
        # Place missile off-screen at first
        self.rect.center = (-100, 100)
        self.dx = 0
        self.dy = 0
        # Allows it to move
        self.toggle = True

    def fire(self, player_pos):
            self.rect.center = player_pos  # Move Bomb to player.
            self.dy = -BOMB_SpeedY              # Set its velocity.

    def fireRight(self, player_pos):
            self.rect.center = player_pos
            # Rotate it to make it look like it's going to the right
            self.image = pygame.transform.rotate(self.image, 345)
            self.dy = -BOMB_SpeedY
            self.dx = BOMB_SpeedX

    def fireLeft(self, player_pos):
            self.rect.center = player_pos
            # Rotate it to make it look like it's going to the left
            self.image = pygame.transform.rotate(self.image, 15)
            self.dy = -BOMB_SpeedY
            self.dx = -BOMB_SpeedX

    def update(self):
        if self.toggle:
            self.rect.centerx += self.dx
            self.rect.centery += self.dy
            # Remove sprite when it's off-screen to save memory
            if self.rect.bottom < 0:
                self.reset()
        else:
            self.rect.centerx -= 0
            self.rect.centery -= 0

    def get_pos(self):
        return self.rect.center

    def reset(self):
        self.kill()

class Enemy(pygame.sprite.Sprite):
    def __init__(self, position, speed):
        pygame.sprite.Sprite.__init__(self)
        # Load img in folder
        self.loadImage()

        self.frame = 0
        self.delay = 3
        self.pause = 0

        self.image = self.imgList[0]
        self.rect = self.image.get_rect()
        self.rect.center = position
        (self.speedX, self.speedY) = speed
        # Allows ship to move
        self.toggle = True

    def loadImage(self):
        self.imgList = []

        for i in range(1, 9) :
            self.image = pygame.image.load(f"assets\sprites\Enemy_animation\{i}.png")
            self.image = self.image.convert()
            self.transColor = self.image.get_at((1,1))
            self.image.set_colorkey(self.transColor)
            self.image = pygame.transform.scale( self.image, (80, 80))
            self.image = pygame.transform.rotate(self.image, 180)
            self.imgList.append(self.image)

    def update(self):
        # This updates the frame of each sprite to simulate animation
        self.pause += 1
        if self.pause >= self.delay:
            self.pause = 0
            self.frame += 1
            if self.frame >= len(self.imgList):
                self.frame = 0
            self.image = self.imgList[self.frame]

        if self.toggle:
            self.rect.center = (self.rect.centerx + self.speedX, self.rect.centery + self.speedY)
            if self.rect.left < 0:
                self.speedX = abs(self.speedX)
            if self.rect.right > WIDTH :
                self.speedX = -1 * abs(self.speedX)
            if self.rect.top < 0:
                self.speedY = abs(self.speedY)
            if self.rect.top > HEIGHT:
                self.rect.bottom = 0
        else :
            self.rect.center = (self.rect.centerx + (self.speedX - self.speedX), self.rect.centery + (self.speedY - self.speedY))

    def reset(self):
        self.kill()

    def get_pos(self):
        return self.rect.center

class Boss(pygame.sprite.Sprite):
    def __init__(self, position, speed):
        # Get image of the whole spritesheet
        pygame.sprite.Sprite.__init__(self)
        self.imgMaster = pygame.image.load("assets\sprites\Royalty-Free-Game-art-Spaceships-from-Unlucky-Studio.png")
        self.imgMaster.convert()
        misImgSize = (237, 230)
        # Create a surface to draw a section of the spritesheet
        self.image = pygame.Surface(misImgSize)
        self.image.blit(self.imgMaster, (0,0), ( (216, 880) ,(misImgSize)) )
        self.image.set_colorkey( self.image.get_at((1,1)))
        self.image = pygame.transform.scale( self.image, (100, 100))
        (self.speedX, self.speedY) = speed
        # Get rect of sprite
        self.rect = self.image.get_rect()
        self.rect.center = position
        # Count hits recieved
        self.hit = 0
        # delay on shots
        self.shoot_delay = 0
        # Allows ship to move
        self.toggle = True

    def update(self):
        if self.toggle:
            # Set a shoot delay. Count to X(30 tick), then reset the timer.
            # The timer is used to tell the program to shoot a enemyMissile from
            # Boss shoots a missile every X amount of seconds.
            # CLOCK_TICK = 1 second
            self.shoot_delay += 1
            if self.shoot_delay > (CLOCK_TICK * 3):
                self.shoot_delay = 0
            # Move it each time update runs. Ex. If CLOCK_TICK = 30,
            # the update runs 30x per second(30FPS)
            self.rect.center = (self.rect.centerx + self.speedX, self.rect.centery + self.speedY)
            if self.rect.left < 0:
                self.speedX = abs(self.speedX)
            if self.rect.right > WIDTH :
                self.speedX = -1 * abs(self.speedX)
            if self.rect.top < 0:
                self.speedY = abs(self.speedY)
            if self.rect.top > HEIGHT:
                self.rect.bottom = 0
        else :
            self.rect.center = (self.rect.centerx + (self.speedX - self.speedX), self.rect.centery + (self.speedY - self.speedY))

    def reset(self):
        self.kill()

    def get_pos(self):
        return self.rect.center

class EnemyMissile(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        # Get image of the whole spritesheet
        self.imgMaster = pygame.image.load("assets\sprites\Royalty-Free-Game-art-Spaceships-from-Unlucky-Studio.png")
        self.imgMaster.convert()
        misImgSize = (45, 77)
        # Create a surface to draw a section of the spritesheet
        self.image = pygame.Surface(misImgSize)
        self.image.blit(self.imgMaster, (0,0), ( (163, 1956) ,(misImgSize)) )
        self.image.set_colorkey( self.image.get_at((1,1)))
        self.image = pygame.transform.scale( self.image, (15, 35) )
        self.image = pygame.transform.rotate(self.image, 180)
        # Get rect of sprite
        self.rect = self.image.get_rect()
        # Place missile off-screen at first
        self.rect.center = (-100, 100)
        self.dx = 0
        self.dy = 0
        # Allows it to move
        self.toggle = True

    def fire(self, enemy_pos):
            # Move Bomb to an enemy
            self.rect.center = enemy_pos
            # Set its velocity to shot it
            self.dy = 10

    def fireRight(self, enemy_pos):
            self.rect.center = enemy_pos
            # Rotate it to make it look like it's going to the right
            self.image = pygame.transform.rotate(self.image, 15)
            self.dy = 10
            self.dx = 5

    def fireLeft(self, enemy_pos):
            self.rect.center = enemy_pos
            # Rotate it to make it look like it's going to the left
            self.image = pygame.transform.rotate(self.image, 345)
            self.dy = 10
            self.dx = -5

    def update(self):
        if self.toggle:
            self.rect.centerx += self.dx
            self.rect.centery += self.dy
            # Remove sprite when it's off-screen to save memory
            if self.rect.top > HEIGHT:
                self.reset()
        else:
            self.rect.centerx += 0
            self.rect.centery += 0

    def get_pos(self):
        return self.rect.center

    def reset(self):
        self.kill()

class Explosion(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
        self.imgMaster = pygame.image.load("assets\sprites\Royalty-Free-Game-art-Spaceships-from-Unlucky-Studio.png")
        self.imgMaster.convert()
        misImgSize = (105, 111)
        # Create a surface to draw a section of the spritesheet
        self.image = pygame.Surface(misImgSize)
        self.image.blit(self.imgMaster, (0,0), ( (416, 2530) ,(misImgSize)) )
        self.image.set_colorkey( self.image.get_at((1,1)))
        self.image = pygame.transform.scale( self.image, (50, 50))
        # Get rect of sprite
        self.rect = self.image.get_rect()
        # Place missile off-screen at first
        self.rect.center = position
        self.delay = 0

    def update(self):
        self.delay += 1
        if self.delay == CLOCK_TICK:
            self.kill()

class MultiShotPowerUp(pygame.sprite.Sprite):
    def __init__(self, position, speed):
        pygame.sprite.Sprite.__init__(self)
        # Get image of the whole spritesheet
        self.imgMaster = pygame.image.load("assets\sprites\powerUpSpriteSheet.png")
        self.imgMaster.convert()
        misImgSize = (135, 100)
        # Create a surface to draw a section of the spritesheet
        self.image = pygame.Surface(misImgSize)
        self.image.blit(self.imgMaster, (0,0), ( (656, 37),(misImgSize)) )
        self.image.set_colorkey( self.image.get_at((1,1)))
        self.image = pygame.transform.scale( self.image, (40, 40) )
        self.image = pygame.transform.rotate(self.image, 237)
        # Get rect of sprite
        self.rect = self.image.get_rect()
        # Place missile off-screen at first
        self.rect.center = position
        (self.dx, self.dy) = speed
        # Allows it to move
        self.toggle = True
        # Set what type of powerUp it is
        self.powerUpType = "multiShot"

    def update(self):
        if self.toggle:
            # Move it each time update runs. Ex. If CLOCK_TICK = 30,
            # the update runs 30x per second(30FPS)
            self.rect.center = (self.rect.centerx + self.dx, self.rect.centery + self.dy)
            if self.rect.left < 0:
                self.dx = abs(self.dx)
            if self.rect.right > WIDTH :
                self.dx = -1 * abs(self.dx)
            if self.rect.top < 0:
                self.dy = abs(self.dy)
            # Remove sprite when it's off-screen to save memory
            if self.rect.top > HEIGHT:
                self.reset()
        else:
            self.rect.centerx += 0
            self.rect.centery += 0

    def get_pos(self):
        return self.rect.center

    def reset(self):
        self.kill()

class SpeedPowerUp(pygame.sprite.Sprite):
    def __init__(self, position, speed):
        pygame.sprite.Sprite.__init__(self)
        # Get image of the whole spritesheet
        self.imgMaster = pygame.image.load("assets\sprites\powerUpSpriteSheet.png")
        self.imgMaster.convert()
        misImgSize = (131, 125)
        # Create a surface to draw a section of the spritesheet
        self.image = pygame.Surface(misImgSize)
        self.image.blit(self.imgMaster, (0,0), ( (0, 37),(misImgSize)) )
        self.image.set_colorkey( self.image.get_at((1,1)))
        self.image = pygame.transform.scale( self.image, (40, 40) )
        # Get rect of sprite
        self.rect = self.image.get_rect()
        # Place missile off-screen at first
        self.rect.center = position
        (self.dx, self.dy) = speed
        # Allows it to move
        self.toggle = True
        # Set what type of powerUp it is
        self.powerUpType = "speedBoost"

    def update(self):
        if self.toggle:
            # Move it each time update runs. Ex. If CLOCK_TICK = 30,
            # the update runs 30x per second(30FPS)
            self.rect.center = (self.rect.centerx + self.dx, self.rect.centery + self.dy)
            if self.rect.left < 0:
                self.dx = abs(self.dx)
            if self.rect.right > WIDTH :
                self.dx = -1 * abs(self.dx)
            if self.rect.top < 0:
                self.dy = abs(self.dy)
            # Remove sprite when it's off-screen to save memory
            if self.rect.top > HEIGHT:
                self.reset()
        else:
            self.rect.centerx += 0
            self.rect.centery += 0

    def get_pos(self):
        return self.rect.center

    def reset(self):
        self.kill()

class Label(pygame.sprite.Sprite):
    def __init__(self, textStr, center, fontName, fontSize, textColor):
        pygame.sprite.Sprite.__init__(self)
        self.text = textStr
        self.center = center
        self.font = pygame.font.Font(fontName, fontSize)
        self.textColor = textColor
    # self.update() - Render the text on the label and any changes to it.
    def update(self):
        self.image = self.font.render(self.text, 1, self.textColor)
        self.rect = self.image.get_rect()
        self.rect.center = self.center

def titleScreen():
    # Construct a background
    background = pygame.image.load("assets\\background\Space_Parallax.png")
    # Convert for better preformance
    background = background.convert()
    background = pygame.transform.scale(background, screen.get_size())
    # screen.blit(background, (0, 0))
    moveY = 0
    # Construct labels for title, objective and controls.
    # It stays until user proceeds or quits game.
    # Adding them to a group is one step needs to update any changes made to them and collision detection
    title = Label("Space Defenders!", ( (WIDTH//2), (HEIGHT//2) ), defaultFont, 25, WHITE)
    goal = Label("Fight off alien ships as long as possible to gain the highest score!", ( (WIDTH//2), ((HEIGHT//2) + 30) ), defaultFont, 23, WHITE)
    instr = Label("Move using arrow keys and use spacebar to shoot", ( (WIDTH//2), (HEIGHT//2) + 60), defaultFont, 25, WHITE)
    startGametxt = Label("Click to start!", ( (WIDTH//2), (HEIGHT//2) + 90), defaultFont, 25, WHITE)
    labelGroup = pygame.sprite.Group(title, goal, instr, startGametxt)
    # Set FPS of the game
    clock = pygame.time.Clock()
    # Set a loop that keeps running until user quits or proceeds
    keepGoing = True
    while keepGoing:
        # Set FPS of the game - 30 frames per second/tick
        clock.tick(CLOCK_TICK)
        # Create Scrolling background
        rel_moveY = moveY % background.get_rect().height
        screen.blit(background, (0, rel_moveY - background.get_rect().height))
        if rel_moveY < HEIGHT:
            screen.blit(background, (0, rel_moveY))
        moveY += 1
        # Handle any events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                keepGoing = False
        # Update the display
        labelGroup.update()
        # Draw the display
        labelGroup.draw(screen)
        # Put it to the screen
        pygame.display.flip()

def game():
    # Construct a background
    background = pygame.image.load("assets\\background\Space_Parallax.png")
    # Convert for better preformance
    background = background.convert()
    background = pygame.transform.scale(background, screen.get_size())
    # screen.blit(background, (0, 0))
    moveY = 0
    # Play music when game starts
    pygame.mixer.music.load("assets\sounds\POL-galactic-trek-short.wav")
    pygame.mixer.music.play(-1) # play continuously
    # Set volume, scaled from 0 to 1
    pygame.mixer.music.set_volume(0.1)
    # Create a necessary objects
    player = Player()
    explodeSound = pygame.mixer.Sound("assets\sounds\\16-bit-explosion_120bpm_C_major.wav")
    tickCount = 0
    highScore = 0
    level = 0
    highScoreLabel = Label(f"Highscore: {highScore}", (100, 50), defaultFont, 25, WHITE)
    levelLabel = Label(f"Level: {level}", ((WIDTH - 100), 50), defaultFont, 25, WHITE)
    pausedLabel = Label(f"", ((WIDTH//2), (HEIGHT//2)), defaultFont, 65, WHITE)
    # Add them to groups
    playerGroup = pygame.sprite.Group(player)
    missileGroup = pygame.sprite.Group()
    powerUpGroup = pygame.sprite.Group()
    enemyGroup = pygame.sprite.Group()
    enemyMissileGroup = pygame.sprite.Group()
    bossGroup = pygame.sprite.Group()
    allEnemyGroup = pygame.sprite.Group(enemyGroup, bossGroup)
    labelGroup = pygame.sprite.Group(highScoreLabel, levelLabel, pausedLabel)
    # - a variable that tells if the user won
    win = False
    # Pause
    pause = False
    # - Set FPS of the game
    clock = pygame.time.Clock()
    #  - Set a loop that keeps running until user quits or proceeds
    keepGoing = True
    while keepGoing:
        # Set FPS of the game - 30 frames per second/tick
        clock.tick(CLOCK_TICK)
        # Every 30 ticks is a second
        tickCount += 1
        # Create Scrolling background
        rel_moveY = moveY % background.get_rect().height
        screen.blit(background, (0, rel_moveY - background.get_rect().height))
        if rel_moveY < HEIGHT:
            screen.blit(background, (0, rel_moveY))
        moveY += 1
        # Set a var that will be a rand interger from X to X
        powerUpChance = randint(1, 100)
        # If it "powerUpChance" is X, spawn it. This will it a X% spawn rate
        if powerUpChance == 1 and pause == False :
            positionX = randint( 0, WIDTH)
            positionY = randint( 0, 50 )
            speedX = randint(3, 5)
            speedY = randint(3, 5)
            multiShotPowerUp = MultiShotPowerUp((positionX, positionY), (speedX, speedY))
            powerUpGroup.add(multiShotPowerUp)

        if powerUpChance == 1 and pause == False :
            positionX = randint( 0, WIDTH)
            positionY = randint( 0, 50 )
            speedX = randint(3, 5)
            speedY = randint(3, 5)
            speedPowerUp = SpeedPowerUp((positionX, positionY), (speedX, speedY))
            powerUpGroup.add(speedPowerUp)

        # Create New list of enemy for each level
        if len(enemyGroup) == 0 and len(bossGroup) == 0:
            level += 1
            levelLabel.text = f"Level: {level}"
            # Every 15 lvls, spawn 2 bosses
            if level % 15 == 0:
                for i in range(2):
                    positionX = randint( 0, WIDTH)
                    positionY = randint( 20, 50 )
                    speedX = random.choice([-10, 10])
                    speedY = 0
                    boss = Boss((positionX, positionY), (speedX, speedY))
                    bossGroup.add(boss)
            elif level % 5 == 0:
                positionX = randint( 0, WIDTH)
                positionY = randint( 20, 50 )
                speedX = random.choice([-10, 10])
                speedY = 0
                boss = Boss((positionX, positionY), (speedX, speedY))
                bossGroup.add(boss)
            else :
                for i in range(ENEMYAMOUNT):
                    positionX = randint( 0, WIDTH)
                    positionY = randint( -20, 0 )
                    speedX = randint(-ENEMYSPEED, ENEMYSPEED)
                    speedY = randint(3, ENEMYSPEED)
                    eachEnemy = Enemy((positionX, positionY), (speedX, speedY))
                    enemyGroup.add(eachEnemy)
        # Pause everything in the game. Game is paused if "paused == True", otherwise run.
        # Also put "paused" text in the middle of screen when "paused == True"
        if pause == True:
            pausedLabel.text = f"Paused"
            player.toggle = False
            for eachPowerUp in powerUpGroup:
                eachPowerUp.toggle = False
            for eachEnemy in enemyGroup:
                eachEnemy.toggle = False
            for eachBoss in bossGroup:
                eachBoss.toggle = False
            for missile in missileGroup:
                missile.toggle = False
            for enemyMissile in enemyMissileGroup:
                enemyMissile.toggle = False
        else:
            pausedLabel.text = f""
            player.toggle = True
            for eachPowerUp in powerUpGroup:
                eachPowerUp.toggle = True
            for eachEnemy in enemyGroup:
                eachEnemy.toggle = True
            for eachBoss in bossGroup:
                eachBoss.toggle = True
            for missile in missileGroup:
                missile.toggle = True
            for enemyMissile in enemyMissileGroup:
                enemyMissile.toggle = True
        # Handle any events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
            if event.type == pygame.KEYDOWN:
                # Space-bar shoots missile
                # If player gets power up, shoot 3 missiles!
                if event.key == pygame.K_SPACE and pause == False and player.multiShot == True:
                    # 1st missile
                    missile = Missile()
                    missileGroup.add(missile)
                    missile.fire(player.get_pos())
                    # 2nd missile
                    missile2 = Missile()
                    missileGroup.add(missile2)
                    missile2.fireRight(player.get_pos())
                    # 3rd missile
                    missile3 = Missile()
                    missileGroup.add(missile3)
                    missile3.fireLeft(player.get_pos())
                elif event.key == pygame.K_SPACE and pause == False:
                    missile = Missile()
                    missileGroup.add(missile)
                    missile.fire(player.get_pos())
                # "P" pauses the game
                if event.key == pygame.K_p and pause == False:
                    pause = True
                elif event.key == pygame.K_p and pause == True:
                    pause = False
        #### Check collisions or any other actions
        # Make a list of enemies, adding them into it when they collide with a missile
        deadEnemy = []
        resetMisList = []

        for eachEnemy in enemyGroup:
            tickCounts = randint(1, 100)
            if tickCounts == 1 and eachEnemy.toggle == True:
                enemyMissile = EnemyMissile()
                enemyMissileGroup.add(enemyMissile)
                enemyMissile.fire(eachEnemy.get_pos())

            if pygame.sprite.spritecollide(eachEnemy, missileGroup, False):
                highScore += 25
                highScoreLabel.text = f"Highscore: {highScore}"
                explodeSound.play()
                explosion = Explosion(eachEnemy.get_pos())
                labelGroup.add(explosion)
                deadEnemy.append(eachEnemy)

        for eachBoss in bossGroup:
            if eachBoss.shoot_delay == 30 and eachEnemy.toggle == True:
                enemyMissile = EnemyMissile()
                enemyMissileGroup.add(enemyMissile)
                enemyMissile.fire(eachBoss.get_pos())
                # Second missile that shoots at an angle
                enemyMissile2 = EnemyMissile()
                enemyMissileGroup.add(enemyMissile2)
                enemyMissile2.fireRight(eachBoss.get_pos())
                # Third missile that shoots at an angle
                enemyMissile3 = EnemyMissile()
                enemyMissileGroup.add(enemyMissile3)
                enemyMissile3.fireLeft(eachBoss.get_pos())

            if pygame.sprite.spritecollide(eachBoss, missileGroup, False):
                eachBoss.hit += 1
                explodeSound.play()
                # If hit X amount of times... add it to add deadEnemy so it can be deleted
                if eachBoss.hit == 20:
                    highScore += 750
                    highScoreLabel.text = f"Highscore: {highScore}"
                    explodeSound.play()
                    explosion = Explosion(eachBoss.get_pos())
                    labelGroup.add(explosion)
                    deadEnemy.append(eachBoss)

        for missile in missileGroup:
            if pygame.sprite.spritecollide(missile, enemyGroup, False) :
                explodeSound.play()
                resetMisList.append(missile)

        for eachPowerUp in powerUpGroup:
            if pygame.sprite.spritecollide(eachPowerUp, playerGroup, False) :
                if eachPowerUp.powerUpType == "multiShot" :
                    # Player hits a power up, allow it to shoot multiple missiles
                    # Set the multishot timer to 0 if the player gets a power up while already
                    # powered up
                    player.multiShot = True
                    player.multiShotTimer = 0
                    eachPowerUp.reset()

                elif eachPowerUp.powerUpType == "speedBoost" :
                    # Player hits this power up, get speed boost
                    # Set the multishot timer to 0 if the player gets a power up while already
                    # powered up
                    player.speedBoost = True
                    player.speedBoostTimer = 0
                    eachPowerUp.reset()
        # Then remove enemy from group
        # If enemy was removed too soon. The the loop above wouldn't detech any collisions
        for eachEnemy in deadEnemy:
            eachEnemy.reset()
        # Then remove missile from group
        for eachMis in resetMisList:
            eachMis.reset()
        # End game if player collides with an enemy
        if pygame.sprite.spritecollide(player, enemyGroup, True) :
            keepGoing = False
        # End game if player collides with an enemy's missile
        if pygame.sprite.spritecollide(player, enemyMissileGroup, True) :
            keepGoing = False
        # Player wins if they get to lvl X
        if level == 50:
            keepGoing = False
            win = True
        # Update and draw/render all the groups
        playerGroup.update()
        missileGroup.update()
        powerUpGroup.update()
        enemyGroup.update()
        enemyMissileGroup.update()
        bossGroup.update()
        allEnemyGroup.update()
        labelGroup.update()

        playerGroup.draw(screen)
        missileGroup.draw(screen)
        powerUpGroup.draw(screen)
        enemyGroup.draw(screen)
        enemyMissileGroup.draw(screen)
        bossGroup.draw(screen)
        allEnemyGroup.draw(screen)
        labelGroup.draw(screen)

        pygame.display.flip()

    return (win, highScore)

def playAgain(winLose):
    # Construct a background
    background = pygame.Surface(screen.get_size())
    # Convert for better preformance
    background = background.convert()
    # Set background color
    background.fill( (0, 0, 0) )
    screen.blit(background, (0, 0))

    # Get from variable "game()" and check if it's true or false.
    # winLose = True, user won.  winLose = False, user lost.
    winOrLose = winLose[0]
    highScore = winLose[1]

    if winOrLose == True :
        gameResult = Label("You Won!", ( (WIDTH//2), (HEIGHT//2)), defaultFont, 25, BLUE)
        highScoreTxt = Label(f"Highscore: {highScore}", ( (WIDTH//2), (HEIGHT//2) + 30), defaultFont, 25, BLUE)
        playAgainTxt = Label("Play again(Y/N)", ( (WIDTH//2), (HEIGHT//2) + 60), defaultFont, 25, BLUE)
    else:
        gameResult = Label("You Lost!", ( (WIDTH//2), (HEIGHT//2)), defaultFont, 25, RED)
        highScoreTxt= Label(f"Highscore: {highScore}", ( (WIDTH//2), (HEIGHT//2) + 30), defaultFont, 25, RED)
        playAgainTxt= Label("Play again(Y/N)", ( (WIDTH//2), (HEIGHT//2) + 60), defaultFont, 25, RED)

    labelGroup = pygame.sprite.Group(gameResult, playAgainTxt, highScoreTxt)

    replay = False
    # Set FPS of the game
    clock = pygame.time.Clock()
    # Set a loop that keeps running until user quits or proceeds
    keepGoing = True
    while keepGoing:
        # Set FPS of the game - 30 frames per second/tick
        clock.tick(CLOCK_TICK)
        # Handle any events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_y:
                    keepGoing = False
                    replay = True
                if event.key == pygame.K_n:
                    keepGoing = False
                    replay = False
                if event.key == pygame.K_q:
                    keepGoing = False
                    replay = False
                    sys.exit()

        labelGroup.clear(screen, background)

        labelGroup.update()

        labelGroup.draw(screen)

        pygame.display.flip()

    return replay

def endCredits():
    # Construct a background
    background = pygame.Surface(screen.get_size())
    # Convert for better preformance
    background = background.convert()
    # Set background color
    background.fill( (0, 0, 0) )
    screen.blit(background, (0, 0))

    endCreditsTxt1 = Label("I hope you enjoyed playing!", ( (WIDTH//2), (HEIGHT//2)), defaultFont, 25, WHITE)
    endCreditsTxt2 = Label("By Daeshaun Morrison", ( (WIDTH//2), (HEIGHT//2) + 30), defaultFont, 25, WHITE)

    labelGroup = pygame.sprite.Group(endCreditsTxt1, endCreditsTxt2)

    # Set FPS of the game
    clock = pygame.time.Clock()
    # Set a loop that runs for a set amount of secounds, 5 in this case.
    keepGoing = True
    frames = 0

    while keepGoing == True:
        clock.tick(CLOCK_TICK)

        frames = frames + 1
        if frames == 150:
            keepGoing = False

        labelGroup.clear(screen, background)

        labelGroup.update()

        labelGroup.draw(screen)

        pygame.display.flip()

def main():
    #####
    titleScreen()

    replay = True
    while replay == True:
      result = game()
      replay = playAgain(result)

    endCredits()

main()
pygame.quit()


from pkg_resources import parse_version
import pybullet_utils.bullet_client as bc
import pybullet_data
import pybullet as p2
import subprocess
import time
import numpy as np
from gym.utils import seeding
from gym import spaces
import gym
import math
import logging
import os
import inspect

currentdir = os.path.dirname(
    os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(os.path.dirname(currentdir))
os.sys.path.insert(0, parentdir)

logger = logging.getLogger(__name__)

class pairwiseAircraftEnv(gym.Env):
    metadata = {
        'render.modes': ['human', 'rgb_array'],
        'video.frames_per_second': 50
    }

    def __init__(self, renders=False, actionRepeat=1500, discrete_actions=False):
        # start the bullet physics server
        self._renders = renders
        self._discrete_actions = discrete_actions
        self._render_height = 400
        self._render_width = 420
        self._actionRepeat = actionRepeat
        self._physics_client_id = -1
        self._cam_dist = 40
        self._cam_pitch = -21  # degree
        self._cam_yaw = 200#200  # degree

        if self._renders:
            self._p = bc.BulletClient(connection_mode=p2.GUI)
        else:
            self._p = bc.BulletClient()

        self.v = 0.1111  # nmi/s

        # action space
        self.min_t1 = 100
        self.max_t1 = 400
        self.min_h = 30
        self.max_h = 40
        self.min_t2 = 200
        self.max_t2 = 400

        # max area:  Isosceles Right Triangle

        self.max_area = (self.max_t2 * self.v) ** 2 / 4 * 2

        self.low_action = np.array([self.min_t1, self.min_h, self.min_t2],
                                   dtype=np.float32)  # t1, h, t2
        self.high_action = np.array([self.max_t1, self.max_h, self.max_t2],
                                    dtype=np.float32)

        self.action_space = spaces.Box(self.low_action,
                                       self.high_action,
                                       dtype=np.float32)
        print('high_action', self.high_action)

        # observation space
        self.seperationStatus_min = 3
        self.seperationStatus_max = 100

        self.low_state = np.array([self.seperationStatus_min],
                                  dtype=np.float32)
        self.high_state = np.array([self.seperationStatus_max],
                                   dtype=np.float32)

        self.observation_space = spaces.Box(low=self.low_state,
                                            high=self.high_state,
                                            dtype=np.float32)

        self.seed()
        # self.reset()
        self.viewer = None
        self._configure()

    def _configure(self, display=None):
        self.display = display

    def seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def step(self, action):
        p = self._p
        r1, r2, r3 = 0, 0, 0
        storeClosestPoints = []
        storeOwnerPosition = []
        storeIntruderPosition = []

        t1, h, t2 = action
        print("t1, h, t2", [t1, h, t2])

        maneuver = self.v * t2 / h
        if maneuver > 1:
            area = np.sqrt((self.v * t2)**2 - h**2) * h
            r3 = - area / self.max_area * (h/np.sqrt((self.v * t2)**2 - h**2))
        else:
            r2 = -3

        for i in range(self._actionRepeat):

            p.resetBaseVelocity(self.intruder,
                                linearVelocity=[-self.v * np.cos(61 * np.pi / 180), self.v * np.sin(61 * np.pi / 180),
                                                0],
                                angularVelocity=[0, 0, 0])
            p.resetBaseVelocity(self.owner,
                                linearVelocity=[self.v, 0, 0],
                                angularVelocity=[0, 0, 0])
            if t1 <= i < t1 + t2:
                p.resetBaseVelocity(self.owner,
                                    linearVelocity=[self.v * np.cos(np.arcsin(h / (self.v * t2))), 0,
                                                    self.v * (h / (self.v * t2))],
                                    angularVelocity=[0, 0, 0])
            if t1 + t2 < i < t1 + 2 * t2:
                p.resetBaseVelocity(self.owner,
                                    linearVelocity=[self.v * np.cos(np.arcsin(h / (self.v * t2))), 0,
                                                    -self.v * (h / (self.v * t2))],
                                    angularVelocity=[0, 0, 0])
            if i > t1 + 2 * t2:
                p.resetBaseVelocity(self.owner,
                                    linearVelocity=[self.v, 0, 0],
                                    angularVelocity=[0, 0, 0])

            closetPoints = p.getClosestPoints(self.owner, self.intruder, 100)  # must be put before stepSimulation
            # print("closetPoints", closetPoints)
            ownerPos, ownerOrn = p.getBasePositionAndOrientation(self.owner)
            intruderPos, intruderOrn = p.getBasePositionAndOrientation(self.intruder)
            storeOwnerPosition.append(ownerPos)
            storeIntruderPosition.append(intruderPos)

            if closetPoints:
                closeDistance = closetPoints[0][8]
                storeClosestPoints.append(closeDistance)

            if storeClosestPoints:
                self.state = min(storeClosestPoints)
            else:
                self.state = 95.23294384

            done = False
            contactPoints = p.getContactPoints(self.owner, self.intruder)
            if bool(contactPoints):
                done = bool(contactPoints)
                endType = "failedResolution"
                r1 = -3
                r3 = 0
                print("!!---- End this loop beacuse of collision  -------- ")

            if min(storeClosestPoints) < 5:
                done = min(storeClosestPoints) < 5
                endType = "failedResolution"
                r1 = -3
                r3 = 0
                print("!!---- End this loop beacuse of unsafe distance  -------- ")
                break
            if i == self._actionRepeat - 1:
                done = (i == self._actionRepeat - 1)
                endType = "successfulResolution"
                print("!!---- End this loop beacuse of ended simulation time -------- ")
                break
            if maneuver <= 1:
                done = True
                endType = "failedManuever"
                r2 = -3
                print("!!----End this loop because of the failed manuever ----")
                break
            p.stepSimulation()
            # time.sleep(0.01)

        # 写包含起始冲突的飞行动作过程
        reward = 2 + r1 + r2 + r3
        print('env done+ r1 r2 r3 endType', [done, r1, r2, r3, endType])
        return np.array([self.state]), [reward, r1, r2, r3], done, endType, storeOwnerPosition, storeIntruderPosition

    def reset(self):
        print("-----------reset simulation---------------")

        p = self._p
        p.resetDebugVisualizerCamera(self._cam_dist, self._cam_yaw,
                                     self._cam_pitch, [0, 0, 0])
        p.resetSimulation()
        # print('---reach here---')

        ownerX = -50 #+ np.random.randn(1)
        ownerY = 0
        ownerZ = 0
        ownerQuatenion = p.getQuaternionFromEuler([0, 0,
                                                   90 / 180 * 3.14])  # rad
        self.owner = p.loadURDF(
            parentdir+"/gym_pairwiseaircraft/urdfFile/owner.urdf",
            basePosition=[ownerX, ownerY, ownerZ],
            baseOrientation=ownerQuatenion,
            globalScaling=2)
        p.resetBaseVelocity(self.owner,
                            linearVelocity=[0.1111, 0, 0],
                            angularVelocity=[0, 0, 0])

        intruderX = 35
        intruderY = -50
        intruderZ = 0
        intruderQuaternion = p.getQuaternionFromEuler(
            [0, 0, -151 / 180 * 3.14])  # rad
        self.intruder = p.loadURDF(
            parentdir+"/gym_pairwiseaircraft/urdfFile/intruder.urdf",
            basePosition=[intruderX, intruderY, intruderZ],
            baseOrientation=intruderQuaternion,
            globalScaling=2)

        p.resetBaseVelocity(self.intruder,
                            linearVelocity=[-self.v * np.cos(61 * np.pi / 180), self.v * np.sin(61 * np.pi / 180),
                                            0],
                            angularVelocity=[0, 0, 0])

        self.timeStep = 1

        p.setTimeStep(self.timeStep)
        # 0 to disable real-time simulation, 1 to enable
        p.setRealTimeSimulation(0)
        closetPoints = p.getClosestPoints(self.owner, self.intruder, 100)
        # print('closetPoints', closetPoints)
        if closetPoints:
            self.state = closetPoints[0][8]
        else:
            self.state = 95.23294384

        return np.array([self.state])

    def render(self, mode='human', close=False):
        if mode == "human":
            self._renders = True
        if mode != "rgb_array":
            return np.array([])
        base_pos = [0, 0, 0]

        view_matrix = self._p.computeViewMatrixFromYawPitchRoll(
            cameraTargetPosition=base_pos,
            distance=self._cam_dist,
            yaw=self._cam_yaw,
            pitch=self._cam_pitch,
            roll=0,
            upAxisIndex=2)
        proj_matrix = self._p.computeProjectionMatrixFOV(fov=60,
                                                         aspect=float(self._render_width) /
                                                                self._render_height,
                                                         nearVal=0.1,
                                                         farVal=100.0)
        (_, _, px, _, _) = self._p.getCameraImage(
            width=self._render_width,
            height=self._render_height,
            renderer=self._p.ER_BULLET_HARDWARE_OPENGL,
            viewMatrix=view_matrix,
            projectionMatrix=proj_matrix)
        rgb_array = np.array(px)
        rgb_array = rgb_array[:, :, :3]
        return rgb_array

    def configure(self, args):
        pass


""" Module handling queries pertaining to bloodwork """
import uuid
from datetime import datetime

from models.medication import MedicationBloodworkTypes
from database.connection import get_cursor

def get_requests_by_practice(practice_id: str, completed: bool = False):
    """ Get the requests in a practice. """
    query = ''
    if completed:
        query = """SELECT request_id, request_type, br.prescription_id, practice_id, completed_at, rp.username, md.medication_name, u.name, i.name as pharmacy_name
                FROM blood_requests br
                JOIN repeat_prescriptions rp USING (prescription_id)
                JOIN medications md USING(medication_id)
                JOIN users u USING (username)
                JOIN institutions i on br.practice_id = i.institution_id
                WHERE practice_id = %s;"""
    else:
        query = """SELECT request_id, request_type, br.prescription_id, practice_id, completed_at, rp.username, md.medication_name, u.name, i.name as pharmacy_name
                FROM blood_requests br
                JOIN repeat_prescriptions rp USING (prescription_id)
                JOIN medications md USING(medication_id)
                JOIN users u USING (username)
                JOIN institutions i on br.practice_id = i.institution_id
                WHERE practice_id = %s AND completed_at IS NULL;"""
    with get_cursor() as cursor:
        cursor.execute(query, (practice_id,))
        return cursor.fetchall()

def get_request_by_id(request_id: str) -> dict:
    """ Get bloodwork request by its ID """
    with get_cursor() as cursor:
        cursor.execute("""SELECT request_id, request_type, prescription_id, practice_id, completed_at FROM blood_requests
                        WHERE request_id = %s;""", (request_id,))
        return cursor.fetchone()

def get_request_by_id_in_practice(request_id: str, practice_id: str) -> dict:
    """ Get bloodwork request by its ID in a given practice """
    with get_cursor() as cursor:
        cursor.execute("""SELECT request_id, request_type, br.prescription_id, practice_id, completed_at, rp.username, md.medication_name, u.name, i.name as pharmacy_name
                FROM blood_requests br
                JOIN repeat_prescriptions rp USING (prescription_id)
                JOIN medications md USING(medication_id)
                JOIN users u USING (username)
                JOIN institutions i on br.practice_id = i.institution_id
                        WHERE request_id = %s AND practice_id = %s;""", (request_id, practice_id))
        return cursor.fetchone()

def create_bloodwork_request(
    prescription_id: str,
    practice_id: str,
    request_type: MedicationBloodworkTypes
) -> str:
    """ Create a request for bloodwork to be completed """
    with get_cursor() as cursor:
        request_id = str(uuid.uuid4())
        cursor.execute("""INSERT INTO blood_requests
                        (request_id, request_type, prescription_id, practice_id)
                        VALUES (%s, %s, %s, %s);""",
                        (request_id, request_type, prescription_id, practice_id)
                    )
        return request_id

def mark_bloodwork_request_complete(request_id: str):
    """ Mark a blood work request as complete """
    with get_cursor() as cursor:
        completed_at = datetime.utcnow().isoformat()
        cursor.execute("""UPDATE blood_requests SET completed_at = %s WHERE request_id = %s;""", (completed_at, request_id,))


import threading

def jobs1():
  global A
  for i in range(10):
    A += 1
    print('job1', A)

def jobs2():
  global A
  for i in range(10):
    A += 10
    print('job2', A)

if __name__ == '__main__':
  A = 0
  t1 = threading.Thread(target=jobs1)
  t2 = threading.Thread(target=jobs2)
  t1.start()
  t2.start()
  t1.join()
  t2.join()


"""This class creates a decision tree classifier for a given dataset."""

from copy import deepcopy

import pandas as pd
from node import Node
from split import Split
from tree import Tree

class DecisionTree:
    """Represents a decision tree classifier."""

    def __init__(self):
        """Creates a decision tree that can be fit to data."""
        self.tree = None

    def __str__(self):
        """Ignores DataFrames, only prints splits."""
        new_tree = deepcopy(self.tree)
        for node in [n for n in list(new_tree) if n is not None]:
            if len(node.val) == 1 or node.val[1] is None:
                node.val = "<data>"
            else:
                node.val = str(node.val[1])
        return str(new_tree)

    @classmethod
    def gini_vector(cls, col):
        """Given a single input Series with a single column filled with the digits 0-n, computes the
    Gini impurity."""
        impurity = 0
        n = len(set(col))
        if n <= 1:  # sets without any elements or with a single class are pure by default
            return 0

        for i in range(n):
            p_class = sum(col.map(lambda x: 1 if x == i else 0)) / len(col)
            impurity += p_class ** 2

        return 1 - impurity

    @classmethod
    def gini(cls, classes):
        """Computes the Gini impurity of the given data, a class vector or matrix in 0's and 1's or any
    additional amount of classes.
        """
        return classes.apply(cls.gini_vector).sum()

    def gen_splits(self, data, classes):
        """Given input data and a corresponding matrix of classes, returns a list of Split objects
        corresponding to all the possible splits of that dataset at that time."""
        splits = []
        for predictor_name in data.columns:
            pred = data[predictor_name]
            # generate every possible split cutoff that would mean something
            # also, remove duplicates
            cutoffs = list(set(pred))
            cutoffs = [c for c in cutoffs if c != max(cutoffs)]  # remove topmost element
            splits += [Split(predictor_name, cutoff) for cutoff in cutoffs]
        return splits

    def execute_split(self, data, classes, split):
        """Given data with associated classes and split data, returns a tuple ((data_false, classes_false)
        (data_true, classes_true)) with the entries that are false and the entries that satisfy the
        split.
        """

        # generate indices of cutoffs instead of just the data to preserve class data
        good_indices = [i for i in range(data.shape[0]) if split.split(i, pd.DataFrame(data), True)]
        bad_indices = [i for i in range(data.shape[0]) if i not in good_indices]

        good = (data.iloc[good_indices, :], classes.iloc[good_indices, :])
        bad = (data.iloc[bad_indices, :], classes.iloc[bad_indices, :])
        return (bad, good)

    def test_split(self, data, classes, split):
        """Given data with associated classes and a split, returns the sum of the Gini impurity of
        the child nodes of this split if it is effected."""

        left, right = self.execute_split(data, classes, split)
        return self.gini(left[1]) + self.gini(right[1])

    def not_overfit(self, data, classes, split, k=0.05):
        """This method can be overridden or replaced to change how much the fitting algorithm prunes the
        tree. The default is to require that at least a fraction of the input be separated (the
        defaut is 5%)."""
        left, right = self.execute_split(data, classes, split)

        return k <= left[0].shape[0] / data.shape[0] <= 1 - k

    def create_split(self, pos, prune_func):
        """Given a position in the decision tree (1-based breadth-first indexing), first checks to see if
        the data is pure. If it is, then it sets both children to None and stops. Otherwise, it
        computes the split that minimizes the Gini impurity for each child node and executes it,
        creating two new children that are the results of the split (left for false, right for
        true).

        Prune_func is the pruning function to determine whether a split is valid.
        """
        # print("Creating split at position {}".format(pos))

        node = self.tree[pos-1]
        node_val = node.val[0]  # the tuple (data, classes)
        curr_gini = self.gini(node_val[1])

        if curr_gini == 0:  # data is pure
            self.tree.set_child(pos, 0, None)
            self.tree.set_child(pos, 1, None)  # make this a leaf
            return None  # we're done here!
        else:  # generate possible splits
            splits = self.gen_splits(*node_val)
            splits = [split for split in splits if prune_func(*node_val, split)]
            # print(splits)
            if len(splits) == 0:  # no splits that separate into at least one class
                self.tree.set_child(pos, 0, None)
                self.tree.set_child(pos, 1, None)
                return None
            # find the best one
            best = min(splits, key=lambda split: self.test_split(*node_val, split))
            # print(sorted(splits, key=lambda split: self.test_split(*node_val, split))[:5])
            if len(node.val) == 1:  # no split in the node yet
                node.val.append(best)
            else:  # overwrite a split
                node.val[1].append(best)
            # execute it
            left, right = self.execute_split(*node_val, best)
            # print(best)
            # print(left[0].shape[0], right[0].shape[0])

            self.tree.set_child(pos, 0, Node([left], None, None))
            self.tree.set_child(pos, 1, Node([right], None, None))

    def recursively_create_splits(self, pos, prune_func):
        """For the current Node, recursively splits all of its children (generating them as it goes) until
        all of the leaves are pure, returning None. Prune_func is the function to reduce
        overfitting.

        """
        if pos > len(list(self.tree)) or self.tree[pos-1] is None:  # we stop here
            return None
        else:  # create two children and recurse to split them
            self.create_split(pos, prune_func)
            self.recursively_create_splits(pos * 2, prune_func)  # left child
            self.recursively_create_splits(pos * 2 + 1, prune_func)  # right child
            return None

    def fit(self, X, y, pruning_func=None):
        """Fits a given input matrix to a given output vector using a decision tree. X should be a DataFrame
        of numerical variables with any index or column names. y should be a vector or matrix with
        the same height as X, as many columns as classes to predict, and each column should be a
        list of 0's and 1's for a given class. The pruning_func parameter can be filled with any
        function that takes in three arguments (data, classes, split) and returns True if that split
        is allowed and False otherwise. The default, specified with None, is to only use splits that
        separate 5% of the input.

        """
        # start with just the entire dataset at the root, with no split
        # I'm going to represent a single node of a tree as a value [(data, classes), split]
        # where data and classes are "what's left" and split is the Split function that gives the
        # left and right children, or None if the data is pure or the splits have stopped
        if pruning_func is None:  # use default
            pruner = self.not_overfit

        self.tree = Tree(Node([(X, y)], None, None))
        self.recursively_create_splits(1, prune_func=pruner)  # pretty anticlimactic
        return None

    def __predict_vec(self, X):
        """Given an input vector with the required number of predictors, returns an output vector
        y_hat representing the predicted classes for all of the classes it was fit to."""

        if self.tree is None:  # nothing to predict with!
            raise ValueError("Must fit to a model before prediction!")

        curr_pos = 1
        while True:  # loop until something is returned
            curr_node = self.tree[curr_pos-1]
            curr = curr_node.val
            if len(curr) == 1 or curr[1] is None:  # no split, so must be pure or close
                # return the mean of the classes, rounding to integers
                # this way, even with non-pure pruned leaves you still get a result
                if not hasattr(curr[0][1], "__len__"):  # single number
                    return curr[0][1]  # we're done
                else:  # lots of values
                    if len(curr[0][1].shape) > 1:  # 2D array, so use array functions
                        return curr[0][1].mean().round()
                    else:  # just get the mean and use basic round, not a method
                        return int(curr[0][1].mean())
            else:  # go down to the correct child and try again
                if curr[1].split(0, X, use_iloc=True):  # split with the input data

                    curr_pos = curr_pos * 2 + 1  # right child

                else:  # left child, because the Split returned False
                    curr_pos *= 2

            # now, just repeat until we get to a leaf!

    def predict(self, X):
        """For every row in X, predicts the classes and returns a matrix Y representing the
        predicted classes."""
        if len(X.shape) == 1:  # single row
            return self.__predict_vec(X)

        return X.apply(self.__predict_vec, axis=1)

    def __score_col(self, col1, col2):
        """Returns the accuracy score for two Series."""
        correct = 0
        for i in range(len(col1)):
            if col1.iloc[i] == col2.iloc[i]:
                correct += 1
        return correct / len(col1)

    def score(self, X, true_y):
        """Gets the accuracy score of the classifier given the X as input and with true_y as the
        actual values. If there are multiple classes, returns an iterable of each accuracy score."""

        preds = self.predict(X)
        if len(preds.shape) >= 2 and preds.shape[1] > 1:  # multiple classes
            scores = []
            for colname in preds:
                scores.append(self.__score_col(preds[colname], true_y[colname]))
            return scores
        else:  # just one class
            if isinstance(preds, pd.DataFrame):  # convert to Series
                converted_pred = preds.iloc[:, 0]
            else:
                converted_pred = preds

            if isinstance(true_y, pd.DataFrame):  # convert to Series
                converted_true = true_y.iloc[:, 0]
            else:
                converted_true = true_y

            return self.__score_col(converted_pred, converted_true)


# -*- coding: utf-8 -*-
from 台湾言语工具.基本物件.词 import 词
from 台湾言语工具.基本物件.组 import 组
from 台湾言语工具.基本物件.集 import 集
from 台湾言语工具.基本物件.句 import 句
from 台湾言语工具.基本物件.章 import 章
from 台湾言语工具.解析整理.型态错误 import 型态错误
from 台湾言语工具.正规.阿拉伯数字 import 阿拉伯数字
from 台湾言语工具.解析整理.拆文分析器 import 拆文分析器

class 拄好长度辞典揣词:
    分析器 = 拆文分析器()
    数字 = 阿拉伯数字()

    # 字词组集句=>句
    # 章=>章
    @classmethod
    def 揣词(cls, 辞典, 物件):
        return cls.揣词分析(辞典, 物件)[0]

    @classmethod
    def 揣词分析(cls, 辞典, 物件):
        if isinstance(物件, 章):
            return cls.章揣词(辞典, 物件)
        字阵列 = 物件.筛出字物件()
        词组合阵列, 分数, 词数 = cls._字阵列揣词(辞典, 字阵列)
        return cls._词组合阵列转句物件(词组合阵列), 分数, 词数

    @classmethod
    def _字阵列揣词(cls, 辞典, 字阵列):
        if False and hasattr(辞典, '空'):
            字阵列 = cls.字阵列改数字(辞典, 字阵列)
        揣词结果 = []
        # from multiprocessing import Pool
        for 所在 in range(len(字阵列)):
            揣词结果.append(辞典.查词(词(字阵列[所在:所在 + 辞典.上济字数()])))
        # 分数 顶一个位置 有啥物词通用
        分数表 = [(0, None, None)] * (len(字阵列) + 1)
        for 所在 in range(len(字阵列)):
            if 所在 != 0 and 分数表[所在][0] == 0:
                词物件 = 词([字阵列[所在 - 1]])
                词物件.属性 = {'无伫辞典': True}
                分数表[所在] = (分数表[所在 - 1][0] + 1, 所在 - 1, {词物件})
            for 揣词长度 in range(len(揣词结果[所在])):
                if 揣词结果[所在][揣词长度] != set():
                    新分数 = 分数表[所在][0] + 1.0 / (揣词长度 + 1)
                    if 分数表[所在 + 揣词长度 + 1][0] == 0 or 新分数 < 分数表[所在 + 揣词长度 + 1][0]:
                        分数表[所在 + 揣词长度 + 1] = (新分数, 所在, 揣词结果[所在][揣词长度])
        上尾 = len(字阵列)
        if 上尾 != 0 and 分数表[上尾][0] == 0:
            词物件 = 词([字阵列[上尾 - 1]])
            词物件.属性 = {'无伫辞典': True}
            分数表[上尾] = (分数表[上尾 - 1][0] + 1, 上尾 - 1, {词物件})
# 		print(len(字阵列), len(揣词结果), len(分数表))
# 		print((字阵列), (揣词结果), (分数表), sep = '\n')
        挑出来 = []
        目前所在 = len(字阵列)
        while 目前所在 is not None and 目前所在 != 0:
            挑出来.append(分数表[目前所在][2])
            目前所在 = 分数表[目前所在][1]
        return 挑出来[::-1], -分数表[len(字阵列)][0], len(挑出来)

    @classmethod
    def 章揣词(cls, 辞典, 章物件):
        if not isinstance(章物件, 章):
            raise 型态错误('传入来的毋是章物件：{0}'.format(str(章物件)))
        标好章 = 章()
        用好句 = 标好章.内底句
        总分 = 0
        总词数 = 0
        for 一句 in 章物件.内底句:
            断好句物件, 分数, 词数 = cls.揣词分析(辞典, 一句)
            用好句.append(断好句物件)
            总分 += 分数
            总词数 += 词数
        return 标好章, 总分, 总词数

    @classmethod
    def _词组合阵列转句物件(cls, 词组合阵列):
        # 因为爱保留属性，所以爱用附加的
        句物件 = 句()
        集阵列 = 句物件.内底集
        for 词组合 in 词组合阵列:
            集物件 = 集()
            组阵列 = 集物件.内底组
            for 词物件 in sorted(词组合, key=str):
                组物件 = 组([词物件])
                if hasattr(词物件, '属性'):
                    组物件.内底词[0].属性 = 词物件.属性
                组阵列.append(组物件)
            集阵列.append(集物件)
        return 句物件

    @classmethod
    def 字阵列改数字(cls, 辞典, 字阵列):
        改字了字阵列 = []
        for 字物件 in 字阵列:
            if cls.数字.是数量无(字物件.型):
                数量 = cls.数字.转数量(辞典.空, 字物件.型)
                组物件 = 拆文分析器.建立组物件(数量)
                改字了字阵列 += cls.筛仔.筛出字物件(组物件)
            elif cls.数字.是号码无(字物件.型):
                号码 = cls.数字.转号码(辞典.空, 字物件.型)
                组物件 = 拆文分析器.建立组物件(号码)
                改字了字阵列 += cls.筛仔.筛出字物件(组物件)
            else:
                改字了字阵列.append(字物件)
        return 改字了字阵列

臺灣言語工具/斷詞/拄好長度辭典揣詞.py


import re

product_info = input()
total_amount = 0
furniture_name = list()
expression = r"\>{2}(?P<product_name>\w+)\<{2}(?P<price>\d+(\.\d+)?)\!(?P<quantity>\d+)"
while product_info !="Purchase":
    matches_product = re.finditer(expression, product_info)
    for match in matches_product:
        product_name = match.group("product_name")
        price = match.group("price")
        if "." in price:
            price.replace(".", ",")
        quantity = match.group("quantity")
        furniture_name.append(product_name)
        total_amount += float(price) * int(quantity)

    product_info = input()
print("Bought furniture:")
for furniture in furniture_name:
    print(furniture)
print(f"Total money spend: {total_amount:.2f}")

Fundamentals/Regular Expressions - Exercise/furniture.py