diff --git a/__pycache__/visualize.cpython-36.pyc b/__pycache__/visualize.cpython-36.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..820f6e2409a620aa5c5826b4e14a0adc6e2499b4
Binary files /dev/null and b/__pycache__/visualize.cpython-36.pyc differ
diff --git a/ai-part-b-master b/ai-part-b-master
new file mode 160000
index 0000000000000000000000000000000000000000..7d1282b7852f205d0eab5ad6c1a48222e04d42ab
--- /dev/null
+++ b/ai-part-b-master
@@ -0,0 +1 @@
+Subproject commit 7d1282b7852f205d0eab5ad6c1a48222e04d42ab
diff --git a/config b/config
new file mode 100644
index 0000000000000000000000000000000000000000..e2c73de79d885cfa3bbd080c991bfedf8fc04152
--- /dev/null
+++ b/config
@@ -0,0 +1,64 @@
+# NEAT configuration for the bit-sequence memory experiment.
+
+# The `NEAT` section specifies parameters particular to the NEAT algorithm
+# or the experiment itself.  This is the only required section.
+[NEAT]
+fitness_criterion     = max
+fitness_threshold     = 600.0
+pop_size              = 100
+reset_on_extinction   = 0
+
+[DefaultGenome]
+num_inputs              = 4
+num_hidden              = 1
+num_outputs             = 1
+initial_connection      = partial_direct 0.5
+feed_forward            = True
+compatibility_disjoint_coefficient    = 1.0
+compatibility_weight_coefficient      = 0.6
+conn_add_prob           = 0.2
+conn_delete_prob        = 0.2
+node_add_prob           = 0.2
+node_delete_prob        = 0.2
+activation_default      = sigmoid
+activation_options      = sigmoid
+activation_mutate_rate  = 0.0
+aggregation_default     = sum
+aggregation_options     = sum
+aggregation_mutate_rate = 0.0
+bias_init_mean          = 0.0
+bias_init_stdev         = 1.0
+bias_replace_rate       = 0.1
+bias_mutate_rate        = 0.7
+bias_mutate_power       = 0.5
+bias_max_value          = 30.0
+bias_min_value          = -30.0
+response_init_mean      = 1.0
+response_init_stdev     = 0.0
+response_replace_rate   = 0.0
+response_mutate_rate    = 0.0
+response_mutate_power   = 0.0
+response_max_value      = 30.0
+response_min_value      = -30.0
+
+weight_max_value        = 30
+weight_min_value        = -30
+weight_init_mean        = 0.0
+weight_init_stdev       = 1.0
+weight_mutate_rate      = 0.8
+weight_replace_rate     = 0.1
+weight_mutate_power     = 0.5
+enabled_default         = True
+enabled_mutate_rate     = 0.01
+
+[DefaultSpeciesSet]
+compatibility_threshold = 3.0
+
+[DefaultStagnation]
+species_fitness_func = max
+max_stagnation  = 20
+
+[DefaultReproduction]
+elitism            = 2
+survival_threshold = 0.2
+
diff --git a/tensTest.py b/tensTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..88916fd2825801c50212b553ed48f14a5181395f
--- /dev/null
+++ b/tensTest.py
@@ -0,0 +1,39 @@
+from itertools import cycle
+from numpy.random import randint,choice
+import sys
+import neat
+import os
+import random
+
+GENERATION = 0
+MAX_FITNESS = 0
+BEST_GENOME = 0
+
+def eval_genomes(genomes, config):
+	i = 0
+	global SCORE
+	global GENERATION, MAX_FITNESS, BEST_GENOME
+
+	GENERATION += 1
+	for genome_id, genome in genomes:
+		
+		genome.fitness = random.randint(1,10)
+		print("Gen : %d Genome # : %d  Fitness : %f Max Fitness : %f"%(GENERATION,i,genome.fitness, MAX_FITNESS))
+		if genome.fitness >= MAX_FITNESS:
+			MAX_FITNESS = genome.fitness
+			BEST_GENOME = genome
+		SCORE = 0
+		i+=1
+
+
+config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
+                         neat.DefaultSpeciesSet, neat.DefaultStagnation,
+                         'config')
+
+pop = neat.Population(config)
+stats = neat.StatisticsReporter()
+pop.add_reporter(stats)
+
+winner = pop.run(eval_genomes, 30)
+
+print(winner)
\ No newline at end of file
diff --git a/visualize.py b/visualize.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bae1a2a8ed7c4bb15b6ee488f964880992a3b91
--- /dev/null
+++ b/visualize.py
@@ -0,0 +1,197 @@
+from __future__ import print_function
+
+import copy
+import warnings
+
+import graphviz
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+def plot_stats(statistics, ylog=False, view=False, filename='avg_fitness.svg'):
+    """ Plots the population's average and best fitness. """
+    if plt is None:
+        warnings.warn("This display is not available due to a missing optional dependency (matplotlib)")
+        return
+
+    generation = range(len(statistics.most_fit_genomes))
+    best_fitness = [c.fitness for c in statistics.most_fit_genomes]
+    avg_fitness = np.array(statistics.get_fitness_mean())
+    stdev_fitness = np.array(statistics.get_fitness_stdev())
+
+    plt.plot(generation, avg_fitness, 'b-', label="average")
+    plt.plot(generation, avg_fitness - stdev_fitness, 'g-.', label="-1 sd")
+    plt.plot(generation, avg_fitness + stdev_fitness, 'g-.', label="+1 sd")
+    plt.plot(generation, best_fitness, 'r-', label="best")
+
+    plt.title("Population's average and best fitness")
+    plt.xlabel("Generations")
+    plt.ylabel("Fitness")
+    plt.grid()
+    plt.legend(loc="best")
+    if ylog:
+        plt.gca().set_yscale('symlog')
+
+    plt.savefig(filename)
+    if view:
+        plt.show()
+
+    plt.close()
+
+
+def plot_spikes(spikes, view=False, filename=None, title=None):
+    """ Plots the trains for a single spiking neuron. """
+    t_values = [t for t, I, v, u, f in spikes]
+    v_values = [v for t, I, v, u, f in spikes]
+    u_values = [u for t, I, v, u, f in spikes]
+    I_values = [I for t, I, v, u, f in spikes]
+    f_values = [f for t, I, v, u, f in spikes]
+
+    fig = plt.figure()
+    plt.subplot(4, 1, 1)
+    plt.ylabel("Potential (mv)")
+    plt.xlabel("Time (in ms)")
+    plt.grid()
+    plt.plot(t_values, v_values, "g-")
+
+    if title is None:
+        plt.title("Izhikevich's spiking neuron model")
+    else:
+        plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))
+
+    plt.subplot(4, 1, 2)
+    plt.ylabel("Fired")
+    plt.xlabel("Time (in ms)")
+    plt.grid()
+    plt.plot(t_values, f_values, "r-")
+
+    plt.subplot(4, 1, 3)
+    plt.ylabel("Recovery (u)")
+    plt.xlabel("Time (in ms)")
+    plt.grid()
+    plt.plot(t_values, u_values, "r-")
+
+    plt.subplot(4, 1, 4)
+    plt.ylabel("Current (I)")
+    plt.xlabel("Time (in ms)")
+    plt.grid()
+    plt.plot(t_values, I_values, "r-o")
+
+    if filename is not None:
+        plt.savefig(filename)
+
+    if view:
+        plt.show()
+        plt.close()
+        fig = None
+
+    return fig
+
+
+def plot_species(statistics, view=False, filename='speciation.svg'):
+    """ Visualizes speciation throughout evolution. """
+    if plt is None:
+        warnings.warn("This display is not available due to a missing optional dependency (matplotlib)")
+        return
+
+    species_sizes = statistics.get_species_sizes()
+    num_generations = len(species_sizes)
+    curves = np.array(species_sizes).T
+
+    fig, ax = plt.subplots()
+    ax.stackplot(range(num_generations), *curves)
+
+    plt.title("Speciation")
+    plt.ylabel("Size per Species")
+    plt.xlabel("Generations")
+
+    plt.savefig(filename)
+
+    if view:
+        plt.show()
+
+    plt.close()
+
+
+def draw_net(config, genome, view=False, filename=None, node_names=None, show_disabled=True, prune_unused=False,
+             node_colors=None, fmt='svg'):
+    """ Receives a genome and draws a neural network with arbitrary topology. """
+    # Attributes for network nodes.
+    if graphviz is None:
+        warnings.warn("This display is not available due to a missing optional dependency (graphviz)")
+        return
+
+    if node_names is None:
+        node_names = {}
+
+    assert type(node_names) is dict
+
+    if node_colors is None:
+        node_colors = {}
+
+    assert type(node_colors) is dict
+
+    node_attrs = {
+        'shape': 'circle',
+        'fontsize': '9',
+        'height': '0.2',
+        'width': '0.2'}
+
+    dot = graphviz.Digraph(format=fmt, node_attr=node_attrs)
+
+    inputs = set()
+    for k in config.genome_config.input_keys:
+        inputs.add(k)
+        name = node_names.get(k, str(k))
+        input_attrs = {'style': 'filled', 'shape': 'box', 'fillcolor': node_colors.get(k, 'lightgray')}
+        dot.node(name, _attributes=input_attrs)
+
+    outputs = set()
+    for k in config.genome_config.output_keys:
+        outputs.add(k)
+        name = node_names.get(k, str(k))
+        node_attrs = {'style': 'filled', 'fillcolor': node_colors.get(k, 'lightblue')}
+
+        dot.node(name, _attributes=node_attrs)
+
+    if prune_unused:
+        connections = set()
+        for cg in genome.connections.values():
+            if cg.enabled or show_disabled:
+                connections.add((cg.in_node_id, cg.out_node_id))
+
+        used_nodes = copy.copy(outputs)
+        pending = copy.copy(outputs)
+        while pending:
+            new_pending = set()
+            for a, b in connections:
+                if b in pending and a not in used_nodes:
+                    new_pending.add(a)
+                    used_nodes.add(a)
+            pending = new_pending
+    else:
+        used_nodes = set(genome.nodes.keys())
+
+    for n in used_nodes:
+        if n in inputs or n in outputs:
+            continue
+
+        attrs = {'style': 'filled',
+                 'fillcolor': node_colors.get(n, 'white')}
+        dot.node(str(n), _attributes=attrs)
+
+    for cg in genome.connections.values():
+        if cg.enabled or show_disabled:
+            #if cg.input not in used_nodes or cg.output not in used_nodes:
+            #    continue
+            input, output = cg.key
+            a = node_names.get(input, str(input))
+            b = node_names.get(output, str(output))
+            style = 'solid' if cg.enabled else 'dotted'
+            color = 'green' if cg.weight > 0 else 'red'
+            width = str(0.1 + abs(cg.weight / 5.0))
+            dot.edge(a, b, _attributes={'style': style, 'color': color, 'penwidth': width})
+
+    dot.render(filename, view=view)
+
+    return dot