Neural Architecture Search and Game Playing
Neural Architecture Search and Game Playing
This module covers two big ideas. First, how genetic algorithms can design deep neural networks automatically — the line of work from NEAT through Deep NEAT to CoDeepNEAT — with a full worked example. Second, game playing through adversarial search: how to think about games where two or more agents oppose each other, the game tree, the minimax algorithm, and static evaluation functions.
The first half of the lecture finishes the neuroevolution topic: why hand-designing networks hits a human bottleneck, how NEAT evolves networks neuron by neuron, how Deep NEAT raises the level from neurons to whole layers, and how CoDeepNEAT splits evolution into blueprints (macro structure) and modules (micro building blocks). Two worked examples — cat vs dog classification and image captioning — run the whole pipeline from genotype through phenotype construction to deployment. The second half switches to adversarial search: games as formal problems, zero-sum games, game trees, the minimax algorithm with fully worked trees, and static evaluation functions. The lecture ends with exam guidance and the industry applications that tie both halves together.
7.1 Neural Architecture Search and Neuroevolution
7.1.1 Recap of the Last Session
The previous session completed genetic algorithms, applied ant colony optimization (ACO) to the traveling salesman problem, and just started neuroevolution. ACO on TSP is a straightforward problem: if you know all the formulas, you just apply them step by step and you are done — the full TSP example was worked through in that session, step by step with real numbers. The genetic algorithm material from two weeks ago — population generation, fitness evaluation, selection, crossover, mutation — is the vocabulary today's topic is built on, so it is worth recalling it now, because everything in this first half of the lecture is "the same genetic algorithm, applied to a new object: the architecture of a network."
Today we finish the neuroevolution topic (about 45 minutes), then take a break and switch to game playing — a completely separate topic that will fill the second half of the session. Keep the genetic-algorithm loop in mind while reading 7.1; keep it in the drawer while reading 7.2 onwards.
7.1.2 What Neural Architecture Search Is
Hook. Who designs the layers, filters, and connections of the deep networks you have used so far? A human expert. Now ask a harder question: what if the best architecture is one no human would ever think of? Neural architecture search automates the design itself.
Neural architecture search (NAS) is nothing but an application of genetic algorithms: the whole technique is about automatically designing models. You already know the manual way from your deep learning background — humans design architectures by hand: they choose how many convolution layers, what kernel sizes, where to place pooling, how wide the dense layers are. NAS is the family of approaches that automates that design, and the course handout links two research papers on this: the Deep NEAT paper and the CoDeepNEAT paper. Both treat architecture design as an optimization problem and solve it with evolutionary search instead of human intuition.
CoDeepNEAT is an automated method for designing deep neural networks. Traditional AI relies on human experts to design architectures manually; CoDeepNEAT replaces that with evolutionary search, and it is, of course, inspired by natural evolution. Because it uses genetic algorithms, it is an application of genetic algorithms to automatically create deep neural network architectures. Nothing mystical: the same operators you used for function optimization are now applied to network structures.
Why do we need this at all? Two reasons the lecture gives, and both are about limits of the human designer.
Reason 1 — the human bottleneck. Designing architectures manually is genuinely challenging, and humans rely on trial and error plus prior knowledge. That prior knowledge itself limits the exploration of novel architectures. The trap is subtle: the more a designer "knows" about what works, the more the search is confined to variations of known patterns. For example, in medical fields, AI has come up with novel architectures that doctors had never thought of — because humans always think in terms of what they already know, and that prior knowledge can be a bottleneck on creativity. An algorithm applying mutation and crossover keeps creating architectures, and sometimes it stumbles onto something more creative than a human would. An evolutionary search has no "taste" and no preconceptions; it keeps trying combinations a human would discard immediately.
Reason 2 — network complexity. When network complexity increases, manual optimization becomes really impractical — the network is huge and manual tuning is simply not possible. A modern CNN has dozens of layers, each with hyperparameters like filter count, kernel size, stride, padding, activation, and dropout; the number of combinations explodes combinatorially. Nobody can hand-tune that space. That is why we adopt CoDeepNEAT.
Scope — bi-level optimization. CoDeepNEAT works in a bi-level fashion, meaning two steps. The high-level step is designing the architecture, done using genetic algorithms (the high-level search). The lower level is the learned parameters, where you use gradient descent — the actual training of the model. So evolution replaces the designer, not the training algorithm: every candidate architecture is still trained with gradient descent, and its validation performance is the fitness that drives the evolutionary search. The common beginner error is to think "evolution is now the whole learning process" — it is not; it is the design process layered on top of ordinary training.
Real-world: the same human-bottleneck argument applies broadly in industry — any domain where expert-designed solutions repeat known patterns, an evolutionary search can explore beyond them. Automated machine learning (AutoML) systems in production today ship exactly this promise: the pipeline and architecture are searched automatically, and the human sets the budget and the metric.
7.1.3 Neuroevolution in One Word
Neuroevolution is, in a single word, using genetic algorithms to evolve neural networks — deep neural networks specifically. Here is how it works: first create many neural networks — that is the population, created randomly for your problem. Then train each network. Measure their performance — in other words, the fitness. Select the best ones, then perform crossover and mutation. Do these steps look familiar? Population generation, fitness check, selection of a parent, crossover, mutation — this is exactly genetic algorithms, but the genetic algorithm is being used to create a deep neural network automatically.
Three terms carry over from the genetic algorithm material, and it is worth fixing their exact meaning in this setting:
- Population — a set of networks (candidate architectures), generated at random to start, not one network. The population is the search's collection of "individuals."
- Fitness — how good that network performs for a given problem (not generic networks — for your problem). It is a number measured on data, typically classification accuracy on a validation set, so it measures generalization rather than memorization.
- Generation — one cycle of this process: evaluate fitness of the current population, select parents, apply crossover and mutation, form the children that become the next population. Running 50 generations means running this cycle 50 times.
The loop is the same loop from the genetic algorithm lecture; only the "individual" has changed from a bit string to a network architecture.
Q: Does the population-generation concept from genetic algorithms apply to NEAT as well? A: Yes, that is applicable there too — but only at the neuron level, at a smaller size, and not at the complete layer level. That is the only difference. The population concept is universal across all the neuroevolution methods in this lecture: the difference between NEAT, Deep NEAT, and CoDeepNEAT is what a node represents and how the population is organized, not whether a population exists.
7.1.4 NEAT: NeuroEvolution of Augmenting Topologies
NEAT stands for NeuroEvolution of Augmenting Topologies: NE stands for neuroevolution, and AT stands for augmenting topologies, because the method is creating networks, and every network can have a different topology — the way it looks. "Augmenting" means the topology grows: the search starts tiny and adds structure over the generations. NEAT is a method for evolving neural networks automatically instead of designing them manually.
A neural network is represented as a graph — you know graphs from data structures: a graph is a set of nodes and edges. Here nodes represent neurons and edges represent the connections between them. That representation choice is the whole trick: if a network is a graph, then evolving a network means evolving a graph, and graph mutation means adding a node or an edge.
NEAT starts with a very simple network having minimal connections, and over generations it gradually adds new nodes and new connections to increase complexity. New types of nodes keep getting added, and that adds flavor to the new type of network being created. Two ideas in NEAT deserve emphasis:
- Minimal start. Begin with the smallest possible structure (input connected to output, few or no hidden neurons). Complexity is earned, generation by generation, instead of assumed at the start.
- Speciation. Similar networks are grouped into species — this is slightly different from vanilla genetic algorithms: among the generated children, they group those that have similar characteristics, so that in the next evolution the diversity is maintained; the next generation is drawn from different groups of species, not only from one.
Analogy — school sports, not one superstar team. Speciation in NEAT is like a school splitting its players into several teams so that every style of play keeps developing: if only the single best team reproduced, one dominant style would crowd out the rest and the whole gene pool would collapse. Grouping similar networks into species and drawing parents from every species keeps variety alive — different "playing styles" of network architecture all get a chance to survive and recombine. Where the analogy breaks: in biology, species cannot interbreed at all; in NEAT, species are a soft computational grouping, and networks from different species are still free to recombine.
NEAT has limitations, and they matter. NEAT performs well for small and simple neural networks, but it is not effective for deep learning models with many layers. Modern AI systems use deep architectures such as CNNs and LSTMs, which require layer-wise design and complex structures. The key point: NEAT evolves individual neurons and connections, but not fully layered architecture. It works at the individual neuron level, or the connection level, but not at the full-fledged architecture level — and modern AI has moved on to deep architectures. So NEAT can be used for small neural networks, but not for a deep neural network with a complex architecture.
Think about what that means concretely: a deep CNN has millions of parameters arranged in dozens of layers; evolving neuron-by-neuron would take an astronomical number of generations to assemble something like a ResNet block, and the search would mostly wander in useless topologies. This limitation led to advanced approaches: Deep NEAT and CoDeepNEAT. The progression is a ladder: NEAT evolves neurons, Deep NEAT evolves layers, CoDeepNEAT evolves reusable modules plus the blueprint that arranges them.
7.1.5 Deep NEAT: Layers as Nodes
Deep NEAT extends NEAT to evolve deep neural networks with complex, multi-layer architectures. It also uses genetic algorithms. The striking difference is this: in NEAT, nodes represent individual neurons; in Deep NEAT, each node represents an entire layer. Every time we keep generating, we get new types of layers.
Each node stores layer details, including the type — it could be a convolution layer, a dense layer, an LSTM, and so on — and it could also store the hyperparameters used, such as filter size. So the chromosome's node is a mini-specification of a layer. Edges define how layers are connected and represent the flow of data between them. One modification: in NEAT we also store the weights, but here we are not very interested in the weights on the edges; we store the weights at a macro level and the generations are not worried about the weights. The evolution searches structure (which layers, how connected, what hyperparameters) and leaves the millions of weight values to gradient descent — the bi-level split from 7.1.2, now visible concretely: evolution decides the skeleton, training decides the weights.
How Deep NEAT builds a network: the process starts with a chromosome represented as a graph — that is the initial population. For each node, a corresponding neural network layer is created. All layers are connected based on the graph structure. Two wiring problems need rules:
- Multiple inputs. When multiple inputs occur (a layer receives data from several parent layers), they are combined using either a concatenation operation or a summation operation. Concatenation stacks the tensors along the channel dimension; summation adds them element-wise. Which one applies is part of the node's design.
- Size mismatch. When there is a size mismatch (parent layers produce outputs of different sizes), you use either pooling or downsampling to fit it. The parent outputs are brought down to the smallest size so the merge is well defined.
The final output is a complete deep neural network built from the evolved structure.
The problem with Deep NEAT: networks generated by Deep NEAT tend to be random — they are complex and lack clear structure. In contrast, modern models like ResNet or GoogLeNet use repeated and well-organized blocks, with modular design. That modularity is missing in Deep NEAT, because Deep NEAT does not reuse learned structures. Suppose you have created a layer and it is benefiting you — why not reuse it again and again? That is exactly what ResNet and other modern models do, but Deep NEAT does not: it keeps randomly generating new layers generation after generation and never reuses what was learned. That leads to a failure to create repeatable patterns.
The observation is the seed of the next step: successful deep architectures in the wild are made of repeated building blocks. So the need for reusable building blocks to design structured, efficient networks — and that is why CoDeepNEAT came into the picture.
Real-world: ResNet and GoogLeNet are the canonical examples of modular, reusable-block architectures in production deep learning — the same design philosophy CoDeepNEAT tries to learn automatically. A ResNet is, quite literally, the same residual block repeated dozens of times with a few connecting layers; GoogLeNet repeats inception modules. Deep NEAT cannot discover such repetition because nothing in its representation rewards "this module worked, use it again."
7.1.6 CoDeepNEAT: Blueprints, Modules, Genotype, and Phenotype
CoDeepNEAT is an improvisation of Deep NEAT. The full flow: initially everything was manual. Then we came up with using genetic algorithms to automatically build a neural network — that was NEAT. NEAT had its own problems, because it is not really applicable to the real world where we need deep architectures. So we went to Deep NEAT, which was promising — it could generate full layers, not just neurons. But there was one problem: it was just randomly generating layers and not giving importance to the reuse factor. So some authors improvised it — and that is CoDeepNEAT.
CoDeepNEAT improves Deep NEAT by separating evolution into two parts, enabling better structure and reuse. The two parts: the blueprint and the module. The blueprint defines the overall network structure and acts as a skeleton for organizing components. The module is a small neural network that serves as a reusable building block. This approach reflects real-world deep networks — it is similar to ResNet and other models — so it is more scalable, more efficient, and more structured, and it exploits the fact that we are reusing what we had already learned.
The genotype is the representation used in CoDeepNEAT, composed of the two things just discussed: blueprints and modules. The blueprints define the overall network structure; the modules define the reusable neural subnetwork. The blueprint is more like an empty slot — it is not the actual layer, just placeholders — and once we have the modules, we plug and play them into those placeholders however we want. The modules contain the actual layers and the hyperparameters.
The blueprint can also be called the macro structure — macro means bigger, not micro. It represents the high-level structure of the network: input on one side, output on the other, and in between the numbered placeholders (1, 2, 1, 3, and so on) — these are not actual layers; they are only placeholders. Each node represents a module species ID: those numbers are module IDs, and a node does not represent a direct layer — later we plug a whole layer into that empty slot. The edges define the data flow: from input it goes here, and from here on. The modules are the micro architectures: small neural subnetworks — one could be convolutional, one could be an LSTM, they can have different filters, and so on. Modules are evolved independently using subpopulations.
The phenotype is the whole assembly. Once you have completed the two steps — the blueprint, and the individual modules generated using genetic algorithms — you put them together, and that is phenotype construction. The phenotype is the actual neural network used for training; it is built by replacing the blueprint nodes with the selected modules and connecting the modules based on the blueprint edges. The assembly process: you select a blueprint (several blueprints were also generated; for each node, meaning each empty slot, there are choices), choose a module from its species, replace the node with the module, connect them — and you have a full network.
Innovation numbers: in that blueprint there were numbers — those are called innovation numbers. In simple words, an innovation number is just a unique ID assigned to the structural mutations. We have to know which module is which: this module has this ID, that module has that ID, so that when we want to place them in the blueprint, the unique IDs help. Simply put, for every child we generate, or every module we generate, we give it a unique innovation number — it is more like a primary key. Why is it important? Because it helps in correct alignment during crossover. We do not want two modules that are the same to be used for crossover; meaningful recombinations occur when we combine two different modules with different unique innovation numbers. When two parent chromosomes meet for crossover, matching components line up by their innovation numbers, and components present in only one parent are simply inherited. The resulting network gives deep architectures, reusable patterns, and efficient search-space exploration.
Real-world: the whole idea — small modules assembled like building blocks — is like microservices: different people (or teams) can develop different modules using genetic algorithms, and when we do phenotype construction we think about which modules to pick. One person has a bunch of modules ready; another has a macro structure; that person decides which modules to plug in.
The phenotype-construction diagram: the diamond represents the input — where we start — and the bottom blue node is the output, the final prediction or output layer. White nodes represent intermediate connections and flow; yellow nodes are the most important — they indicate the module insertions. Each yellow node is an expanded module or subnetwork: originally each yellow node was a single blueprint node, just a circle, but now it is replaced by a complex module generated using genetic algorithms. The modules can be reused — the same module can be repeated at different positions. So if a module proves fruitful and has good accuracy, we keep reusing it. A module itself is actually a neural network, and connections between modules follow the blueprint structure. In NEAT or Deep NEAT we did not have this concept of blueprint plus modules, and that hampered our ability to reuse things — that is why CoDeepNEAT became more popular.
Analogy — a construction site. Think of a building. The blueprint is the architect's plan: rooms marked as empty slots with labels ("kitchen here, bathroom here"), no actual walls yet. The modules are prefabricated rooms, built off-site by specialist teams, each room a complete functioning unit. Phenotype construction is the assembly: take the plan, lift a prefabricated kitchen into the "kitchen" slot, a bathroom into the "bathroom" slot, connect the plumbing and wiring along the plan's marked routes — and the building now exists and can be occupied (trained). The reuse property maps exactly: a great prefabricated room design can be installed in every apartment of a tower; a great module can be plugged into every empty slot that points to its species. Where the analogy breaks: in construction, installing a room again means building another copy; in CoDeepNEAT, reusing a module means sharing the same learned weights — which is precisely what makes reuse cheap.
Q: Can you again explain the difference between genotype and phenotype? A: Genotype is nothing but a representation. We want to do two things. We want to create a blueprint — a structure having some placeholders: you have an input, you have an output, and in between some placeholders (we will see more complex examples; it will be more clear). This is done. Then you actually create modules, again using genetic algorithms. That step is the genotype representation: I have created my blueprint, I have created my modules. Now you replace those placeholders in your blueprint with the actual modules you want — you pick from different modules that you have generated and keep putting them in, and it creates new architectures. That part is the phenotype — phenotype construction is the actual network creation, where you replace the placeholders in the blueprint with modules. The phenotype is the final deep neural network: you got it by first building the genotype and then replacing those modules. Once you have built that network structure, it can be used for training and fitness evaluation.
So the one-line answer: genotype = the blueprint plus the modules (the representation); phenotype = the assembled, trainable network (the construction). Genotype is the information; phenotype is what that information builds.
Q: Can this be used to create a multi-model architecture? A: Yes, you can do that. Nothing in the representation forces a single chain from input to output — a blueprint is a graph, so it can branch into parallel towers, fuse them again, or emit several outputs. Multi-model and multi-task architectures are simply more elaborate blueprints.
7.1.7 The Genetic Algorithm Steps Inside CoDeepNEAT
We now apply the genetic algorithm itself to generate the modules, generation by generation. This is a procedure, so we walk it as one: what it is for, what goes in and out, and each step with its rationale.
Purpose. The genetic algorithm inside CoDeepNEAT produces the module subpopulations: a pool of small, reusable neural networks of growing quality. It exists to solve the design problem that human experts cannot scale — deciding which micro-architectures are worth plugging into blueprints.
Inputs & outputs. Input: a random initial population of simple module architectures, plus the training/validation data and a fitness measure (for example validation accuracy). Output: after many generations, a set of evolved modules with high fitness, ready to be selected during phenotype construction. The blueprint population is evolved with the same machinery, though in practice the lecture focuses on module evolution.
Step 1 — Initialization. Evolution begins with a population of randomly generated simple neural network structures. These are modules, but they are simple at the beginning. Say we have 100 networks with minimal layers and basic connectivity. These initial networks act as a starting point for the evolutionary search. Starting with simple structures allows gradual complexity growth through mutations and crossover; this approach promotes efficiency and avoids unnecessary complexity in the early stages. (Rationale: a search that starts complex spends its early generations just simplifying; starting minimal means every added element is a deliberate improvement.)
Step 2 — Fitness evaluation. Each candidate architecture is instantiated as a neural network and trained using gradient descent. This is very important: each child, each module, is evaluated separately. Example: a CNN architecture is trained on a cat vs dog dataset containing, say, 10,000 labeled images. Training is performed for a limited number of runs — we do not want to keep doing a lot; use something like 5 to 10 and evaluate the capability of that particular module. The objective is to identify architectures that achieve higher accuracy within limited training time. The fitness is computed as classification accuracy on a validation dataset: if a network correctly classifies 90 out of 100 validation images, the fitness of that network is 90; another module might give 80, another 92. This ensures that evaluation is based on generalization, not memorization. A higher fitness value indicates better architectural suitability — and that module is more likely to get picked up while replacing the blueprint placeholders. (Rationale: limited training epochs act as a speed pressure — the search favours architectures that learn quickly, not ones that would eventually be good after a month of training.)
Step 3 — Selection. Parent architectures are selected using a probabilistic approach based on fitness value — the roulette wheel, the same parent selection we used in genetic algorithms. Take two networks: network A with 90% accuracy and network B with 50%. Convert to proportions — 0.9 and 0.5 — and build the roulette wheel; we normalize with the total fitness value. Each architecture gets a selection probability proportional to its share of the total fitness:
where is the probability that architecture is selected as a parent, (fitness) is the validation performance of architecture , and the sum in the denominator runs over the whole population. Every symbol is named: indexes one candidate, indexes all candidates, is a probability so it always lands in , and the probabilities of the whole population sum to exactly 1. A will occupy most of the pie chart, B a small slice. Spinning the wheel will land on A most of the time — and that is what we want, because A has the higher fitness.
Worked detail — four architectures. Suppose the population has four architectures with fitness values A = 80%, B = 60%, C = 40%, D = 20%. The total fitness is
The selection probabilities are:
Check: . A and B are selected multiple times because they have higher fitness and so a bigger representation in the pie chart — more likely to be selected for the crossover reproduction step. C is occasionally selected (40%), D rarely (small chunk). This maintains a balance of exploitation and exploration: we exploit the fact that A and B are promising, but we do not ignore C and D — sometimes they get chosen, because we want exploration too. Sense-check: if every slot of the wheel is the same size (equal fitness), everyone has probability — pure exploration; the bigger the fitness gap, the more the wheel is biased toward the strong candidates.
Step 4 — Crossover. Two selected parent architectures are combined to produce a new architecture. Simple example: parent one is convolution → pooling → dense; parent two is convolution → convolution → dense. Apply a one-point crossover: draw the line, and you get one child as convolution → convolution → dense and another child as convolution → pooling → dense. One of the children inherits the deeper feature-extraction capability. You can apply any crossover technique; the point is that from the parent architectures you reproduce and build new children. The children are also neural networks, so we assign each child an innovation number so it can be used during phenotype construction — when we replace a module in the blueprint we know which one we are placing and can reuse it again. Each structural component is assigned an innovation number; during crossover, matching components are aligned based on their identifiers — for example, if both parents contain a convolutional layer with similar function, they get aligned together; an extra layer present in only one parent is inherited if it is beneficial. We do eliminate children that are exactly like the parent — same as in genetic algorithms, where the child and parent should not be identical.
Step 5 — Mutation. Mutation introduces changes to explore new architectures. What mutations can we do on a neural network? Add a new layer in the child after crossover — say a new convolutional layer. Add a new connection — a skip connection or something similar. Or modify some parameters — filter size, activation function, and so on. Example: in the parent the filter size was 3×3; in the child we change it to 5×5. That brings the new flavor. Just like in the genetic algorithm material two weeks ago, where we randomly changed bits (1 to 2, 3 to 4) to bring in new flavor, mutation here serves the same purpose. If the original architecture was convolution 3×3 + pooling + dense, during mutation we change to 5×5 — the improved ability to capture larger patterns may increase validation accuracy from 85 to 92, so the child might be more promising than the parent. Keep generating like this.
Step 6 — Speciation. This step alone is slightly extra — it is not in vanilla genetic algorithms; it was introduced by the authors. After the children are generated, we group them into species based on similarity metrics. For example, a bunch of children that are shallow networks (one or two layers) go into one species; deeper networks (three to five layers) go into another species. Why? Because the next step of the genetic algorithm is the next iteration: the children become parents, and we again apply crossover, mutation, and so on. We want to be sure that when children become parents, not everybody is picked only from one species — we pick some parents from species one, some from species two, and so on, preserving diversity. This step is peculiar to this approach; in vanilla genetic algorithms we do not create species.
Step 7 — Generations. The whole generational evolution process repeats across multiple generations; each generation again includes fitness evaluation, selection of parents, crossover, mutation, and putting children into species. Example for cat vs dog classification: in generation one, all the children give roughly 50% accuracy. Keep applying the genetic algorithm: by the 20th generation, around 75% accuracy; by the 50th generation, around 95%. The final architecture is selected based on the highest validation performance — go to the 50th generation, take the modules or children from there, and use them in phenotype construction.
Cost & scope. Every child in every generation is trained — 100 children × 50 generations means thousands of full (short) training runs. That is why this is resource intensive, and why the lecture is explicit that the heavy cost is paid once: during genotype construction. The payoff is that the evolved modules are reusable afterwards — plug them into any blueprint. Related scope note: in the original CoDeepNEAT experiments, the module and blueprint populations were small (for example 25 blueprints and 45 modules), and about 100 assembled networks were trained per generation — the cost scales with population size, generations, and training epochs, all of which are hyperparameters the designer sets.
Q: Introducing modules with limited training may cause generating a poor phenotype or unreliable phenotype predictions. A: That is exactly the point of doing the whole genetic algorithm first. We are not directly replacing a module in the blueprint mid-evolution. Phenotype construction happens once the whole genetic algorithm is completed. When you complete the whole genetic algorithm, you have a lot of children that you generated, and their fitness values are good — your modules are good. Only then do you go and put them in the blueprint. You run the whole genetic algorithm for several generations, and only then do you do the phenotype construction. The order is the safeguard: evolution finishes first, and only the survivors (high-fitness modules) ever reach the assembly stage.
Q: Is a module just one node? A: No. A module is one node in NEAT, but in Deep NEAT or CoDeepNEAT a node is a full-fledged neural network. When you initially start the genetic algorithm, the candidate architecture is small — each population starts with a randomly generated simple neural network architecture. Each node itself is a neural network, but you start small so that later you can slowly increase the complexity. That is what the last line means: this approach promotes efficiency and avoids unnecessary complexity in the early stage. You start with a smaller neural network and slowly evolve it. This is the same misconception in reverse: a blueprint node is an empty slot, a module is a whole small network — never confuse the one-slot position with the network that fills it.
Q: Would it be able to backtrack some training in case offsprings are much weaker? A: At least in this paper they do not, but technically it is possible — self-correcting, backtracking, trying to create offspring again. It may have a huge dynamic impact on hardware infrastructure. Yes, there are critiques of AI about the whole sustainability aspect, the hardware aspect, the cost — all of that applies here too, because you are applying so many computations in one generation and you keep generating 50 times and so on. To get the training accuracy for all children, too much time is required — yes, it is required. But at least you are not manually doing it: the system is automatically creating the network for you, so it is better. Resource intensiveness, yes.
7.1.8 Worked Example: Cat vs Dog Classification
Let us run a full pass through the whole pipeline: input, genotype (module generation via genetic algorithms), phenotype construction, training, and deployment. The example is a two-class image classifier: cat vs dog.
Step A — Input. Suppose we input a cat image of pixels. First we convert it into a tensor. Why the 3? We have converted it into a tensor which is color — RGB. The tensor carries, for each of the 32×32 pixel positions, three numbers: the red, green, and blue intensities of that pixel. We take the RGB and normalize it (scale the intensities into a fixed range, such as ) so training is numerically stable. At this stage the system does not know it is a cat; it knows only the numbers — 32×32×3 = 3,072 input values with no meaning attached.
Step B — The evolved architecture is selected. CoDeepNEAT has already evolved a network like module 1, module 2, module 3. How did it get those modules? It did the genetic algorithm: it initialized, ran the steps, and got those three modules. Module 1 is convolutional plus ReLU; module 2 is convolution 5×5 plus pooling; module 3 is a dense layer. This structure was not manually designed — it was discovered through evolution. This step — generating the modules or micro architectures — is what the CoDeepNEAT paper calls the genotype. The phenotype is the assembled chain: module 1 → module 2 → module 3, wired end to end along the blueprint.
Step C — Testing the modules. Layer 1 has a 3×3 kernel; it detects the edges of the body outline and so on, and the output is a feature map highlighting edges. Layer 2 is the activation — ReLU, which is like a max of 0 and the input value: , keeping positive responses and zeroing negatives. Layer 3 is convolution — texture detection; it combines the edges to detect patterns like fur and the eye region, and the network starts distinguishing cat-like texture. Then pooling — max pooling reduces the size but keeps the strongest features. The focus is Boolean: is this feature present or not — is fur there or not, is a triangular ear there or not. Then the higher-level features: triangular ears, small nose, whisker lines. All the extracted features are flattened into a vector, and this will give lines like: if whiskers are there, if triangular ears are there, then it is more likely a cat. Then we have the output — a classification that says, say, and . This should sum to one: . The final prediction: cat, with 92% confidence, given by the output layer. Sense-check: the two probabilities are non-negative and add to exactly 1, so they form a valid distribution over the two classes, and the network commits to the class with the larger probability.
Step D — What happens during training. If the prediction is wrong — for example the true label is dog but the model predicted cat — we calculate the error. That is nothing but the loss. The loss is predicted minus actual — , where (y-hat) is the predicted value and is the actual label — and the weights are updated using gradient descent. The lecture states this simple form deliberately, as a signed error: it tells you the size of the mistake and its direction (over-predicted, +; under-predicted, −). Standard training loss functions build on exactly this difference:
- Squared error — used for regression; it makes large errors cost much more than small ones and removes the sign.
- Cross-entropy — used for classification with softmax outputs; it punishes confident wrong answers heavily.
The relationship: all three are smallest when matches , and all three are minimized by gradient descent, so the professor's simplified form captures the essential mechanics (direction and size of the error) that the standard forms refine. In this pipeline, gradient descent updates each weight by a step proportional to the derivative of the loss with respect to it, , with (eta) the learning rate. All of this happens while we are creating the node — that node itself is nothing but the module creation. When we use genetic algorithms and create a new child, we evaluate that new child's fitness value; that is what these steps are — testing with sample validation data.
Step E — Evolution picks the best. During evolution, suppose there was a network A that can only detect edges, with accuracy 60, and a network B that detects both fur and ears, with accuracy 85. What would we do? And there is a network C, after mutation: we picked these as parents, mutated, and after mutation it adds a skip connection and the accuracy becomes 92. Evolution selects network C as the best, and again it generates the next generations, and so on.
Worked selection logic. Three candidates: A = 60, B = 85, C = 92 (fitness = validation accuracy). Total = 60 + 85 + 92 = 237, so , , . C is the most promising, and indeed the mutation that produced C (adding a skip connection over some layers) is the kind of structural change that creates the residual-style shortcuts seen in modern architectures. C is kept as the best module of this generation and continues to reproduce in the next generations.
Step F — Deployment. When deployed, the input image is converted into numbers, it passes through the evolved architecture — the final phenotype, the whole blueprint already containing the modules — and the features are extracted automatically, and the final answer is given to us. The system does not see animals like us: it detects edges, textures, parts, objects, and so on. CoDeepNEAT ensures all of this is done automatically — using the blueprint and the module, where the module is created using genetic algorithms and the fitness function is how good that one network is when tested on some sample images.
Q: How is this different than a CNN? A: Only for the generation of modules — the rest are the same. The generation is done using a genetic algorithm, and that is the whole heart of this. That is why we say CoDeepNEAT is an application of genetic algorithms. Otherwise it is all the same — it is still supervised. The network you end up with is still a CNN: convolutions, ReLU, pooling, dense layer, softmax output, trained with gradient descent on labeled images. The only novel part is who designed the architecture — evolution instead of a human. That is the point of the whole lecture segment: same CNN, different designer.
Q: Can we control the number of randomly generated nodes? A: Of course you can. This is state of the art — the research paper came out only a bunch of years back — and all these extra additions can be done. Designing the architecture is done automatically, so there is no human design. That is correct. Population size, initial complexity, mutation operators, generation count — all of these are knobs the designer sets; evolution runs inside the bounds they choose.
Q: Considering the network complexity and these trials, this will be too resource intensive. A: Yes, but only during the genotype construction. Once you have all the promising modules, you can reuse them — you can put them in any possible blueprint and reuse them. So overall, it is still a promising approach. The expensive phase is a one-time investment; the reusable modules pay it back every time they are plugged into a new blueprint.
Q: Can you let me know the threshold fitness that is considered? A: That depends on the use case — in which type of problem you are using it. It is totally up to the designer. There is no universal "good enough" number: a medical screening model may demand 99%+ accuracy before deployment, while a recommendation model may be fine at 75%. The fitness threshold is a business decision, not an algorithm constant.
7.1.9 Worked Example: Image Captioning with CoDeepNEAT
A company wants to automatically generate captions for uploaded images using CoDeepNEAT. The system evolves CNN modules for image feature extraction, LSTM modules for text generation, and blueprints for arranging the modules. Three modules are already with us: a CNN feature-extraction module, an LSTM layer, and a dense plus softmax block. A blueprint is also given, with placeholders M1, M2, M3 — we should replace those M1, M2, M3 with the modules. The initial validation scores are given as BLEU scores — BLEU is the bilingual evaluation understudy, the caption score. For each candidate you have the scores. (BLEU compares a generated caption against reference captions by counting matching word n-grams — a score in — so it is a natural fitness measure for caption quality.)
What is asked in this problem: how do we identify whether we need blueprint evolution or module evolution only? It will be given to us. In most cases we generate the modules only using evolution, because that is the heart of it — the blueprint is just plugging things in. We can use some existing blueprints; very rarely do we generate the blueprint as well. Then: show the first iteration of phenotype construction, fitness evaluation, selection, crossover, and mutation.
First iteration, step by step. Because this is genetic algorithms, we can assume any initial population. The example assumes the population (M1, M2, M3) and (M1, M1, M3). The blueprint was already given, so we take it up and construct the architectures — replace M1 with the CNN, M2 with the LSTM, M3 with the dense block. So the two candidate networks are:
- N1 = CNN → LSTM → dense+softmax (from (M1, M2, M3))
- N2 = CNN → CNN → dense+softmax (from (M1, M1, M3))
Then evaluate the fitness functions and compare with the actual bilingual scores that were given; compute the best network. Why was N2 picked as the best network in this case? Because comparing the given threshold with our fitness, N2 is the most promising one. Pick it, and then do the selection — generate parents from it. Then the next thing is crossover: from CNN → LSTM → dense (the first parent) and CNN → CNN → dense (the second parent), apply single-point crossover. We get CNN → CNN → dense and CNN → LSTM → dense — two children. From there, perform some mutation: change the 3×3 filter to 5×5, and we get a new child — that one is the mutated child. Then put them into some species, and finally keep going like that — that completes one full step of genetic algorithms, and the evolution is shown.
The exam-style version of this problem asks you to do this whole first iteration yourself: assume a random initial population, calculate the fitness, keep doing selection, crossover, mutation — the exact sequence worked above.
After several generations, CoDeepNEAT discovers optimal modules and the best blueprint structure. From this we sometimes evolve the blueprint too, and sometimes the blueprint stays fixed and only the modules are evolved. After that it can also tune the hyperparameters — every iteration is tweaking them — so it can give a high-performing deep neural network architecture automatically. This type of network is deployed for tasks like image generation, classification, speech recognition, medical analysis, and so on.
Exam note: solve this problem yourself at that point — assume a random initial population, calculate the fitness, keep doing selection, crossover, mutation. "If I were you, I will try to attempt that whole process." The answer is present in the course materials; the theory behind it — genotype, phenotype, limitations — is worth exploring fully. The image captioning problem is the worked practice for CoDeepNEAT: be able to reproduce the first iteration — phenotype construction, fitness evaluation, selection, crossover, mutation — with real numbers of your own choosing.
Q: I think mutation is also a choice, not as a mandate. Can you confirm? A: No — mutation is a part of the genetic algorithm. If you come up with your own new research paper, that is different, but the vanilla genetic algorithm has a mutation step, and since CoDeepNEAT uses the genetic algorithm, mutation will be done — it is mandated here. Why do we do mutation? You get the bigger idea: after the crossover step, the child has only the characteristics of parent one and parent two. That is not true in real evolution. The child might have — say both parents have nothing to do with creativity, both are software engineers — the child can end up with some characteristics that are more artistic and become an artist. That happens in the real world. We want that in these algorithms too: we do not want the child to be just the crossover of the parents. Crossover shuffles what exists; mutation invents what does not exist yet — without it, the search can never leave the gene pool it started with.
Q: We are not using species classification here. Where is it used? A: In this example I have not shown it, because in the question it was not asked. If you see, only the mutation was asked. But after mutation, you can have the species grouping also — you group some of the children into one species and the other children into another. Here we just generated two children, so there is no need of putting them into species — each one of them becomes one species. If children are the same, then mutation is required; otherwise there is no point in going ahead. And anyway, mutation is always done — even if the child is the same or not, we will do mutation.
Q: What if a child that is expected to give a better result performs poorly? And while selecting modules, if there are dependent modules for the selected one, should those also be considered? A: That is what parent selection sorts out. If it is giving a poor performance, it will not have a bigger share in the roulette wheel — then it might not get selected. Fitness is measured, not assumed: a child that "should" have been better but measures worse simply gets a small wheel slice and is likely to disappear from the population. Dependencies are handled by the same mechanism — a module whose dependent modules fail will score poorly on its own validation performance, and the search drops it.
Q: Can we visualize child network weights using the trained weights of their parent models? A: Yes, you could — but in this paper, I am not sure if they have done it in that way. Technically, when crossover and mutation preserve parts of a parent's structure, the corresponding trained weights could be inherited or initialized from the parent — a technique related to Lamarckian evolution in the neuroevolution literature — but the original CoDeepNEAT paper does not rely on it; children are trained from scratch during fitness evaluation.
Q: If we do crossover in CNN dimensions, do we have a problem? A: That is right — you can actually apply pooling or reduction to make it compatible. When two parents use different kernel sizes or channel counts, the merged child's layers may have mismatched tensor dimensions at the splice point; the fix is the same as in Deep NEAT assembly: insert a pooling or downsampling operation so the sizes line up before the data flows on.
Q: Do we create as many modules as the number of nodes we have, and proceed with different combinations to fill the nodes? A: It depends. Usually we generate many modules, and then we see how many we need in the blueprint — that is the more common approach. But what you propose is also not bad: why do you want to waste computation? You first see the blueprint, decide how many nodes need to get replaced with modules, and generate that many alone. That is also possible. Both workflows are legitimate; the common one is a big module pool (so the same module can be reused in many blueprints), and the lean one is module generation sized exactly to the blueprint.
Q: Do the white nodes of the blueprint help in dimensionality matching? A: Yes — that is the whole intent of it. The blueprint already tells that; when you match it, you will have to choose a module that is a right fit there. Recall the diagram: the white nodes are the intermediate connection points that carry data between module insertions; they encode the shapes and connectivity the assembler must respect, so the modules plugged into neighbouring yellow nodes must be dimensionally compatible — or a pooling/reduction step must be inserted, exactly as in crossover.
Q: Does the genetic algorithm take into account domain heuristics or information? A: It can, but the vanilla version does not. Where would it account for it? Only during the fitness function evaluation — only there it can do that. If you want the search to prefer architectures with, say, small memory footprint for mobile deployment, you fold that penalty into the fitness score; the genetic operators themselves are domain-blind.
7.1.10 Summary of the Topic
Module = reusable subnetwork (a neural network in itself). Blueprint = macro architecture with empty-slot nodes. Genotype = creating the blueprint plus generating modules using evolution; in genotype construction, modules are identified and trained, their fitness is found, and then we apply the genetic algorithm on that fitness — we create patterns, do crossover, and generate new children (children are also called modules in this work). Phenotype = assembling it all: replacing the blueprint placeholders with selected modules. Fitness calculation is nothing but the actual performance of that small module. The blueprint can also be evolved — different types of macro architectures can be constructed — but in the scope of this paper we are more bothered about the modules being generated using evolution. When you find the fitness and apply the genetic algorithm, you create newer types of children; the most promising ones get plugged into the blueprint.
The ladder, in one breath: NEAT evolved neurons → Deep NEAT evolved layers → CoDeepNEAT evolves reusable modules plus a blueprint that arranges them, reusing what evolution already learned. The fitness is validation performance; the machinery is the genetic algorithm from earlier lectures; the payoff is architecture search without the human bottleneck.
Real-world: CoDeepNEAT-style automatic architecture search is applied to image generation, image classification, speech recognition, and medical analysis; the module-reuse philosophy mirrors how ResNet-style blocks are reused across production networks, and the division of labor (someone evolves modules, someone else assembles them) mirrors microservices development. The original CoDeepNEAT paper's own deployment is a working image-captioning system for a magazine website, where the evolved architecture (repeated LSTM modules with summing merges and skip connections) beat a hand-tuned baseline on BLEU and CIDEr scores.
7.2 Adversarial Search and Game Playing
7.2.1 What Adversarial Search Is
Hook. Every search problem so far had one agent looking for its own path. What changes when the environment contains a second agent whose only goal is to stop you? Everything — the search technique itself must change, because your move is no longer the only move.
In all the problems we have seen so far — the search problems — how many agents were there? Only one: that agent was searching for the path, for the goal, and all of that. Now we ask: what if the number of agents is more than one? What if the environment is not fully observable? That is the fundamental difference.
Adversarial search is searching in a setting where two or more agents have conflicting goals. Think of two people playing tic-tac-toe. A girl says: by placing tic-tac-toe, I am learning about chess. Why? Because she knows that if she cannot win, she will make sure the other person is not winning. In chess, that is exactly what we do: we start with the ambition to win, but at some point, if we realize we cannot win, we at least start playing defensively and ensure the other person also does not win — and they go into a draw. (And the other player says: quit stalling and move already.)
So the conflicting goal is not only about attaining the goal yourself; you are also focused on the other agent not reaching the goal. Formally: adversarial search is a branch of AI focused on solving problems where two or more agents or players have opposing goals. In such environments, one player's gain is another's loss — competition. In such problems, an agent cannot plan only by asking: what action gets me closer to my goal? It must also ask: what will my opponent do to reduce my chance of success? This is not only about the final goal — it is not just the goal part; across the whole process, every time you want to make a move, you will think: if I do this, what will that agent do? The whole technique changes. It is not just that if I have won, the other person has lost — that is the outcome. In the process itself, I will keep looking ahead and make sure the other person is not getting closer to the goal than me.
Process vs outcome — the professor's distinction. "If I win, the opponent has lost" describes the outcome of a game. Adversarial search lives in the process: before every move, the agent looks ahead at the opponent's best reply and blocks it. The outcome is determined by the terminal state; the process is the continuous looking-ahead that happens before each action. Keep this distinction — it recurs throughout the lecture and is the subject of a student correction below.
Real-world: adversarial search matters because many use cases have multiple agents with conflicting views. In today's world, people are coming up with multi-agent architectures where one agent has to critique the other — if it successfully critiques, it gets a utility; if the other is not able to, it gets negative utility, and so on. Those all sit in the direction of adversarial search: multiple agents actually competing.
Examples of adversarial problems: tic-tac-toe, chess, Go, checkers — all competitive games. Notice what they share: deterministic rules, clear legal moves, and a definite winner or draw — the ideal laboratory for search.
Why study games at all? The study and design of games enables computers to model the ways in which humans think and act, and simulates human intelligence. Real-world games have a lot of strategy behind them, and AI can learn from that: not just "how do I win," but "what might the opponent move that hampers my winning chances — so I should block that." There is a whole separate field of AI for gaming — interesting and challenging problems, larger search spaces, smaller solutions, exploring better HCI interactions — but that is a completely different field and not what we are covering here. This is a scope warning worth writing down: we study games in AI (search over game trees), not AI for games (designing entertainment software).
Historical note: the old computer chess games from around the year 2000 — those games were designed using the same principles? They were a form of AI but more rule-based: they used rule engines, and that is why it was difficult to beat the computer even back then. The same minimax-style principles, implemented as hand-coded rules rather than learned models.
Characteristics of games that matter: observability — not all games are fully observable (some are fully observable, some partially); stochasticity (is there chance, like dice, or is it deterministic?); time granularity — do we have a time constraint or not; and the number of players. All of these come into play — they decide which game-solving technique applies.
7.2.2 Normal Search vs Adversarial Search
The contrast with everything we learned before:
| Aspect | Normal search | Adversarial search |
|---|---|---|
| Number of agents | Usually one | At least two |
| Goal | Reach a goal state | Win, or maximize my utility |
| Environment | Passive | Strategic and competitive |
| Next action depends on | Only our action | Our action and the opponent's action |
| Example | Route finding | Chess, tic-tac-toe |
| Main issue | Finding the path | Choosing a move while anticipating the opponent's best response |
The rows read as a checklist for classifying any problem: count the agents, check who controls the next action, and the rest follows. If a second agent controls half the moves, "plan the path and walk it" stops working — the path may be blocked before you take the second step.
Also remember the other type of search: in one type, the state itself was the goal — that is local search. Local search is about the state itself being the answer, the solution; not the path towards the goal. In adversarial search the main issue is choosing a move while anticipating the opponent's best response. Three-way contrast to keep straight: path search (normal search) finds a route to a goal; local search finds the goal state itself (optimization over states); adversarial search finds a move under an opponent's best response.
7.2.3 A Game as a Formal Problem
When we say "game" in this AI sense, we mean the following six components:
- Initial state — the starting board or situation (call it ).
- Player function — tells whose turn it is (first X, then O, alternating).
- Action function — gives the legal moves from a state.
- Result function — gives the new state after a move.
- Terminal test — checks whether the game is over or not.
- Utility function — assigns a final value to the outcome: if I win, plus one; if I lose, minus one; if I draw, zero.
Worked through for tic-tac-toe: the initial state is . The max player is going to put an X; the min player is going to put an O. At the first move, how many children are possible? Nine — we can put the X in any of the nine cells, and each placement gives a different child. Then we have to put the O. The only constraint: we cannot put the O on top of the X — some other position. How many options? Eight. And similarly, under each of the nine branches, there are eight options where we can put the O, and like this the game keeps going. Wherever we put an X or an O, that becomes our transition and we get the next state. The full tic-tac-toe tree is small enough to draw in principle: fewer than terminal leaves (with many duplicates), which is why it is the standard teaching game.
What is a terminal state in tic-tac-toe? Either X has achieved a row, column, or diagonal, or O has achieved a row, column, or diagonal — if any player achieved those, that is a terminal state. Or the whole board is filled and nobody has won — that is also a terminal state. So the terminal test is: is there a completed row/column/diagonal, or is the board full? — and in both cases the game stops.
What is the utility? From the max player's point of view: if X has won, max gets +1. If it is a draw (nobody won), 0. If O has won (say O completed a column), then from max's point of view it is negative — max gets −1. Written from the min player's point of view, the same three boards flip: the board where X won is −1 for min, the draw is 0, and the board where O won is +1 for min. The utility function always carries the "from whose point of view" qualifier — a board does not have a value in itself, it has a value for each player.
Q: Is win, lose, or tie the result? A: No — very important terminology: win, lose, or tie is not the result. The result is the state we get when we apply an action. For example, this was a state; I applied the action "put O at first row, second column" and I got this new state — that is the result. We have a board configuration (a state); it is O's chance; O places here; the new board is the result state. Win, lose, or tie is determined by the terminal test, and the utility function scores it. So the words have distinct jobs: action → result (a new state); terminal test → outcome (is it over?); utility → score (who benefits, and by how much). A student suggested "the result is win/lose/tie" — it seems plausible because the word "result" in everyday language means the final outcome — but in this formal vocabulary, the result is the state transition output, and win/lose/tie is the score of a terminal state.
So the full formalization of tic-tac-toe: states (just names); two players, X then O alternating; actions = place an X or an O, but in an empty cell only — never on an already existing mark; result = the new state after the action; terminal states = someone completes a row/column/diagonal, or the board fills with no winner; utility = −1, 0, or +1.
This is exactly the problem-solving agent depiction: initial state, action, transition, utility. The game formalization is the search-problem formalization from earlier lectures with two additions — a second player alternating turns, and a utility that scores terminal states instead of a single goal check.
7.2.4 Two-Player Zero-Sum Games
Many classic AI game-playing examples assume a two-player, zero-sum game. What does that mean? Two players — that is what "two-player" means. One player's gain is the other player's loss. The players have exactly opposite goals: both want to win, which means the goals are opposite.
We call the players Max and Min. Max is the player trying to maximize the score. Min is the opponent trying to minimize Max's score. Very important: Min is not trying to win directly — Min is trying to minimize Max's score, and by doing that, indirectly maximizing his own chance of winning. If Max gets +1, Min effectively gets −1. Examples: tic-tac-toe, chess, checkers — all two-player games where one player's gain is another person's loss.
The naming is worth dwelling on: "Max" and "Min" describe optimization behaviour, not personalities. Max runs the max operation, Min runs the min operation — and because the scores are opposite, minimizing Max's score is identical to maximizing Min's own. In a zero-sum game the two goals collapse into one number.
Real-world: many life situations are zero-sum — an interview where three people compete for one position; a promotion. One person's gain is another person's loss. A three-player game might not be zero-sum — but it is still, in a way, one player's gain being the other two players' loss. (Technically: in two-player zero-sum games the payoffs of any terminal state add to zero, so a single number describes both players; with three or more players you need a utility tuple, one component per player — the lecture returns to this in 7.3.)
How is tic-tac-toe zero-sum at each step when the results come at the end? At each step, you are trying to make a move that benefits you — or, in other words, makes the benefit smaller for the opponent. Suppose someone has a configuration with two marks in a row; it is your chance with the O. Where do you put the O? You put it to block their victory. They can still win later — that is a different story — but you try to block them in every move so that they also do not end up moving towards victory. That is why it is a zero-sum game: in every move you carry the notion that ultimately you want to win, or at least you want to make sure the opponent is not going towards victory.
The blocking move, concretely. Board: X in the top-left and top-middle — X has two marks in a row. It is O's turn. If O ignores the threat and plays somewhere random, X completes the top row next move and wins. O plays the top-right cell: the row is blocked. That single move did not win the game for O, but it removed X's immediate win — O's gain is exactly X's loss (−1 for X's plan, +1 for O's survival). Repeat this reasoning at every move and you get the zero-sum behaviour: each step converts "the opponent gets closer to +1" into "the opponent stays at 0."
7.2.5 Game Trees
A game tree represents all possible sequences of moves; at each level, the players alternate. In tic-tac-toe we start from the initial state and go all the way to a terminal state; from the first state there are nine branches, each with eight options, and if you draw the full thing, that tree is called the game tree. The operations vary per level: first a max chance, then a min chance, then a max chance, then a min chance, alternating like that. At max nodes, choose the largest value — because Max is trying to maximize. At min nodes, choose the smallest value — because by minimizing you are minimizing the chance of the max node to win. The values at the leaves represent the final outcomes or estimated scores.
Think of the tree's layout: the root is the initial state; each edge is one legal move; each child is the state after that move; players alternate level by level (level 0: Max, level 1: Min, level 2: Max, ...); and the leaves are terminal states carrying utility values. Reading a game tree is reading a game's entire future at once — which is exactly why the size explodes: tic-tac-toe stays drawable, but chess has a branching factor of about 35 and games run about 80 moves deep, so the full tree has roughly nodes — a number with more than a hundred digits.
For small problems this game tree can be drawn — not a problem. For larger problems, drawing the full game tree is not possible. But the game tree is very important, because a lot of expert players are believed to visualize several levels in their minds: call this level 0, level 1, level 2, level 3 — some experts, like the chess grandmaster Pragyananda, are able to visualize up to, say, level 2 in their mind, for all combinations, so they are always very proactive: if I make this move, what will he move? That is why they are champions. Computers on the other hand — Deep Blue and so on — can visualize even more levels. But the point here is not to completely generate the search tree. The point is that even if we generate only a little, we carry this notion — my loss is his win, his win is my loss — and with that in mind we try to take paths or moves. That is what adversarial search is.
Real-world: Deep Blue (the chess computer) searched many more levels of the game tree than human grandmasters; grandmasters like Pragyananda compensate with pattern knowledge and anticipation. And note: a minimax-style approach is what the old rule-based chess programs of the early 2000s approximated — the same underlying principles, but with rule engines.
7.2.6 Student Questions and Answers
Q: But isn't that one thing? If we reach the goal, then the other won't reach automatically? A: Yes — as an outcome, if you win, the other has lost. But across the whole process also, you keep looking for it. It is not just the goal part. Every time you want to make a move, you think: if I do this, what will that agent do? The whole technique changes. It is not just that if I have won, the other person has lost — that is the outcome. In the process itself, I will keep looking ahead and make sure the other person is not going closer to the goal than me. Several students asked variants of this question — the confusion point is the same every time: outcome-level thinking ("if I reach the goal, the opponent loses automatically") versus process-level thinking ("before every move, anticipate what the opponent will do"). Adversarial search is built on the process-level view.
Q: Is this a foundation of reflexive agents? A: Sort of. But adversarial search itself is an important concept of AI because there are a lot of use cases where there is not just one agent — there are multiple agents with conflicting views, like blocking the opponent. For example, in today's world people are coming up with multi-agent architectures where one of them has to critique the other: if it successfully does, it gets a utility; if the other is not able to, it gets negative utility. Those are all in the direction of adversarial search — multiple agents that are actually competing. Reflexive agents react to the current percept; adversarial search additionally simulates the opponent's reactions — related, but not the same mechanism.
Q: How is tic-tac-toe a zero-sum game at each step when results come at the end? A: At each step you are trying to make a move that benefits you — in other words, trying to make the benefit lesser for the opponent. Suppose the opponent has two marks in a row; it is your chance. You put your O to block their victory. They can still win later, but you are trying to block them in every move so that they also do not end up winning. That is why it is zero-sum.
Q: Should every move be evaluated, or only terminal positions get values? A: That is what is called a static evaluation, and we will look at that next. Every move is scored by the static evaluation function, and those scores feed the tree. For terminal positions the utility function gives exact values; for non-terminal positions the static evaluation function estimates them — the topic of 7.4.
Q: In the old days (around 2000) we played computer chess. Were those games designed using the same principles we are learning now, or something different? A: Same only. AI became a buzzword now because it had a lot of practical applications — the whole of Transformers happened, ChatGPT happened — but AI has been there. Even those old games had some form of intelligence, but they were more rule-based. They did not use the latest concepts, but they were also some form of AI; they did use rule engines, and that is why it was difficult to beat the computer even back then.
7.3 The Minimax Algorithm
7.3.1 The Core Idea
The minimax algorithm (some people call it mini-max) is the first algorithm we look at for game playing. Minimax chooses the move that gives the best guaranteed outcome, assuming the opponent also plays optimally. Some casual opponents might not play optimally — they might place the X or the O incorrectly and lose the game — but if someone is playing optimally, they will definitely make sure the game ends in a draw, or they win.
The idea: Max chooses the move with the maximum value; Min chooses the move with the minimum value. This value is a utility or static evaluation value (we will learn where those come from after the examples). The values are backed up from the leaves to the root: a max node takes the maximum of its children's values and a min node takes the minimum:
Every symbol is named: is the backed-up value of node , indexes a child of , is the set of 's children, and / pick the largest/smallest of those child values. The recursion starts at the leaves (their values are given by the utility function for terminal states, or by static evaluation for cut-off states) and climbs to the root; the move chosen at the root is the one leading to the child with the root's value.
Intuition — the two-sided number line. Max and Min are each other's mirror images: Max keeps raising the value (taking the largest child), Min keeps lowering it (taking the smallest child), and the tree alternates the two operations level by level. The value that reaches the root is the result of this tug of war — every value Max pushes up, Min immediately tries to drag down at the next level. And here is the interesting line, the heart of minimax: Max does not simply choose the branch with the best possible outcome — it chooses the branch whose worst-case outcome is best.
Minimax is one of the oldest algorithms in AI, used generally for two players. We assign one player as Max, who chooses the maximum from the given set of choices; the other player is Min, who chooses the minimum from the same collection. A tree data structure is used; we designate each level as alternating between min and max, and at each level the decision is made by the player. The last level has all the potential outcomes of the game — or it might be given to you as some static values. It is a recursive algorithm.
Why "worst-case best"? The guarantee rests on a bet about the opponent: minimax assumes Min is perfect. Under that assumption, any branch whose worst leaf is bad is a branch that will actually deliver that bad leaf, because Min will steer there. So Max's only rational choice is the branch whose worst leaf is as high as possible — the best guaranteed outcome. If Min turns out to be weaker than perfect, Max only does better; the guarantee never erodes.
Assumptions & limits. Minimax assumes (1) two players alternating moves, (2) perfect information — both players see the whole board — and (3) a deterministic, zero-sum game where a single utility value per leaf describes both players. When any assumption fails, minimax as described here needs modification: chance (dice) requires expected values over chance nodes; hidden information (poker) requires belief states; three or more players require utility tuples. For the two-player, deterministic, zero-sum, perfect-information setting this course covers, the guarantee "you will never be forced into the worst outcome" holds exactly.
Cost of the full tree. Minimax explores the whole game tree down to the leaves. If the maximum depth is and there are legal moves at every point, the time complexity is — exponential in depth. That is why the lecture emphasizes partial trees: for chess (, moves) the full tree is physically impossible, and real programs cut the tree at some depth and evaluate the cut nodes with static evaluation functions (the topic of 7.4).
Exam note: the minimax examples in this section are the classic style of question — expect to back values up through a small tree by hand. Practice both techniques shown below: the careful in-order traversal with ±∞ initialization, and the direct trick (max node → largest child, min node → smallest child). A minimax numerical question is expected; static evaluation values are handed to you in the problem.
7.3.2 Worked Example 1: A Small Tree
Here is a manual game tree with static evaluation values given at the leaves (assume for now these values are given to you; we will see how they are computed later). The root is a max node; its two children are min nodes; each min node has two leaves.
The tree. Root (max) → two min children; the left min node has leaves 4 and 7; the right min node has leaves 2 and 6.
- Left subtree: the min node looks at its children, values 4 and 7. A min node always tries to minimize, so it takes the minimum: min(4, 7) = 4.
- Right subtree: values 2 and 6 → min(2, 6) = 2.
- Root: now the root is a max node: among the left path (4) and the right path (2), it takes the best, which is 4.
Answer: the best move for max is the left branch, and the guaranteed value is at least 4. It might or might not go to 7: when max moves left, the next chance belongs to the min player, and min knows that max would then pick the 7 — so min does not let him pick it and chooses 4, blocking the way. Max chooses the left branch because even after min's best response, max still gets a value of 4.
Sense-check: the alternative (right branch) bottoms out at 2 no matter how well max plays; 4 beats 2, so the left branch is right — and note the guarantee is about the floor (4), not the ceiling (7).
The goal of the minimax algorithm is not to take you to the 7 — the largest. It tells you: I will not take you to the worst. Values are always built from the bottom up.
7.3.3 The Working: In-Order Traversal with Minus and Plus Infinity
Let us work a full tree step by step with the traversal machinery, because this is a very important concept in minimax. The tree: root A (max node) with children B and C (min nodes); B has children D and E (max nodes); C has children F and G (max nodes). Leaves: D has −1 and 8; E has −3 and −1; F has 2 and 1; G has −3 and 4. That is seven nodes (A, B, C, D, E, F, G), with the layer pattern max → min → max → leaves.
We use two concepts: depth-first search, and in-order traversal. In-order traversal means: if there is a node, we first visit the left child, then the root, then the right child — left, root, right. Combined with depth-first search, this fixes the exact order in which nodes are visited: go left as deep as you can, then come back up one level, then go right.
Initialization with infinities. Before the traversal: for all the max nodes, assign minus infinity (); for all the min nodes, assign plus infinity (). Why? Think about the number line: the max player starts at the most negative possible point and wants to maximize, climbing towards the positive direction; the minimizer starts assuming plus infinity and tries to reduce, going the other direction. So each node starts at the "worst possible" value for its role, and every child comparison either improves it or leaves it alone: a max node's stored value can only climb, a min node's can only descend.
Then apply the traversal and fill the values.
The traversal, step by step.
- From max node A, go to B (min), then left to D (max). D's first child is −1: compare −1 with −∞ — −1 is the maximum, so erase −∞ and store −1. Then there is another child: compare 8 with −1 — 8 is the maximum, so erase −1 and store 8. D = 8.
- Go back to B. There is still a child on the right (E), but since we are doing left-root-right, we first update B: B is a min node, currently +∞; minimize: +∞ → 8 (8 is smaller than +∞). Now go to E: first child −3 vs −∞ → −3; second child −1 vs −3: on the number line, −1 is bigger than −3, so E = −1.
- Now at the min node B we have 8 (from D's side) and −1 (from E's side): among 8 and −1, which is smaller? −1. Erase 8, store −1. B = −1. Back at A, store −1.
- Now the right side. F: children 2 and 1 → max = 2. G: children −3 and 4 → max = 4. C is a min node: min(2, 4) = 2. A is a max node: max(−1, 2) = 2.
Final values: A = 2, B = −1, C = 2, D = 8, E = −1, F = 2, G = 4.
The small trick (but understand the traversal, do not just apply the trick blindly): at a max node pick the maximum of its children, at a min node pick the minimum, and back the values up: max(children of D) = max(−1, 8) = 8; max(children of E) = max(−3, −1) = −1; min(8, −1) = −1; max(2, 1) = 2; max(−3, 4) = 4; min(2, 4) = 2; max(−1, 2) = 2. Same numbers, both routes — the trick is a shortcut for the traversal, not a substitute for understanding why the min node refuses the 8.
Pitfalls.
- "Max should have taken the 8." In this example many students will say: Max should have gone to this 8. No — minimax never told you it will take you to the best. The 8 lives behind a min node: D = 8, but B (the min node above it) refuses it and reports −1, because B's other child E carries −1 and −3. Max cannot reach the 8 without Min's permission.
- Mixing the roles of the infinities. belongs to max nodes (they start rock-bottom and climb), to min nodes (they start at the top and descend). Swap them and every comparison inverts.
- Applying the trick without the traversal. On an exam, the direct max/min shortcut is fast — but the traversal version is what shows the reasoning, and the lecture warns not to apply the trick blindly when the tree is drawn in an unusual order (for example, a child listed right before a sibling is still visited in left-root-right order).
What does this tell us? The worst outcomes: if max ends up in the −1 subtree, the leaves are −1 and −3 — even worse for max. Minimax says: I will not take you there. Minimax takes you to 2. It never promised the 8, but it promises you will not end up in the worst possible thing.
7.3.4 Worked Example 2: A Deeper Tree
Now a tree with more layers: the layers alternate max, min, max, min. The leaf pairs, as narrated in the walkthrough, are read at max nodes:
The tree. Root (max) → two min nodes → four max nodes → eight leaves. The leaf pairs under the four max nodes are:
Each max node takes the larger of its two leaves. Then two min nodes take the minimum of the values above them: min(7, 8) = 7 and min(8, 9) = 8. Then the root max node: max(7, 8) = 8.
Final answer: the root value is 8 — the move that avoids the worst while not chasing the best. A note on the slide's numbers: the best leaf of the full tree was 15 (the professor's "best possibility is 15 here"); that leaf lies outside the four pairs shown above, which belong to a partial tree. The propagated values 7, 8, 8, 9 → 7, 8 → 8 are exactly as narrated and can be re-derived by direct computation: max(7,3)=7, max(8,−10)=8, max(1,8)=8, max(9,−3)=9, then min(7,8)=7, min(8,9)=8, then max(7,8)=8.
What is the worst utility value here? −10 — and note that −10 was eliminated early: it came up inside the first min node and was cut off there because that was a minimizer node. What is the best possibility? 15. Minimax tells us: I will not take you to 15 — if you go there it is good for you — but I promise you will not end up at −10. How? Because at each step we alternated between the players' moves: max layers picked the maximum of their child pairs, min layers picked the minimum, and that propagation is exactly what eliminated the −10 at the first min level.
The guarantee: minimax will not dump you into the worst possibility. You might end up at the best (15) if the opponent plays badly, but you will never be forced into the worst — and if you do not get the worst or the best but a reasonable choice, that is still better for you. This is exactly what you do in tic-tac-toe: you start the game wanting to win; after a point you say — fine, even if I don't win, I will make sure this person doesn't win. At the start you know you are the maximizer: if you get the 15, grateful, you win. But what if you end up at −10? Then you have lost. So the aim is: let me not go to −10; let me go to 15 if possible; or let me land on 8 or 4 — at least I have not lost the game.
7.3.5 Worked Example 3: A Tree with Three Children
A similar example with multiple children per node.
The tree. A max layer at the root; its children are min layers with three children each.
- The first min node looks at 3, 12, 8 → picks the minimum, 3.
- The second looks at 2, 4, 6 → picks 2.
- The third looks at 14, 5, 2 → picks 2.
- The max layer at the root picks the maximum among 3, 2, 2 → 3.
Answer: the root's minimax value is 3, reached via the first branch. Sense-check: the first branch's floor is 3 — even Min's best effort leaves Max with 3 — while the other two branches can be dragged down to 2. Higher floor, right choice.
The in-order traversal story: we go to the first child, update to 3; then 12 is not less than 3, so we do not update; 8 is not less than 3, so we do not update; the moment this part is done, 3 gets updated at the min node. Then we come to the second min node, put the 2, and so on. We are actually doing in-order traversal and putting the numbers, but as a trick it is simpler to directly pick the minimum values and put them, and the max layer picks the maximum. Notice the traversal detail: at a min node, a child value only overwrites the stored value when it is smaller — 12 and 8 both lose to the 3 already stored, and the node's final value is set only after all children are visited.
7.3.6 Layer Ordering and Multiplayer Games
Does the sequence of min and max layers change as per the use case? No. It depends on where you start: if you start with max, it will be max, min, max, min, max, min...; if you started with min, it will be min, max, min, max, and so on. You cannot change them in between. The alternation is forced by who moves when: the player who moves first determines the root's type, and turns strictly alternate from there — a game cannot hand two consecutive moves to the same player.
Will it always be max-min, or could the decision tree have even more depth? It could be the full tree, but we will always focus on some partial part of the tree — what we show can be a part of a full bigger tree. Every example in this section is a slice of a larger tree: the values that matter are the ones in the shown subtree; the full tree simply continues below (or beside) it.
Multiplayer games (three or more players) are not in scope for this course, but they exist. In a three-player game, the outcome is a tuple of utilities, one per player. Player A looks at the first component, player B at the second, player C at the third, and each chooses the move maximizing their own component.
How the tuple mechanism works. Say the utility of a terminal state is a triple — the value for player A, for player B, and for player C. Suppose it is C's turn at a node with two possible moves, leading to terminal states and . C compares only the third component: 6 vs 3 — 6 is larger, so C picks the first move and the node backs up . In the professor's walkthrough of the same figure: among values like 2 and 1, 2 is larger; among 2, 1, 2, 2 is larger; among 7, 7, 1, 5, 4, 5, the value 5 is larger — each player just looks at their own slot of the tuple. So the rule is: at a node, the player whose turn it is looks only at their own component and maximizes it; the whole tuple of the chosen child is propagated upward. (The concrete tuples above are the canonical textbook example the walkthrough was narrating; the mechanism — each player maximizes their own component — is the essential point, and it is exactly why a three-player game needs a tuple instead of one number.)
7.3.7 Student Questions and Answers
Q: Why didn't minimax take us to the 8 (or the 15)? Many students will say max should have gone to the 8. A: Minimax never told you it will take you to the best value. In the first example the worst outcomes for max are the −1 and −3 leaves; minimax promises you will not end up there — it takes you to 2. In the deeper example it promises you will not end up at −10, and it does not promise 15. Won't dump you into the worst — that is the point. If the opponent plays badly, you might end up at 15 anyway; but you will never be forced into the worst. The professor's vivid summary: minimax will not dump you into the worst — the guarantee is about avoiding the worst outcome, not reaching the best one.
Q: How does the depth of the tree decide the minimax assurance of not going to the worst outcome? A: Depth is not deciding anything. These values are deciding, and the minimax algorithm is deciding. Even if you have a large depth, he will only guarantee you: I will not take you to the worst. The assurance comes from the min/max propagation, not from how tall the tree is: deeper trees compute the same guarantee over more moves.
Q: Does the sequence of min and max layers change as per the use case? A: No, it depends on where you start. If you start with max, it will be max, min, max, min... If you start with min, it will be min, max, min, max... You cannot change them in between.
Q: Will it always be max-min, or could this also have even more depth in the decision tree? A: It could be the full tree, but we will always focus on some partial part of the tree. This example could be a part of a full bigger tree; we are just showing some parts of it, not the full tree.
Q: For a multiplayer game with three or four or more players, the total would be the addition of all this, and then we can do the minimax — am I correct? A: You saw that multiplayer A, B, C example earlier. They will look at their parts and try to choose what is best for them. The total is a sum of numbers, and each chooses what is best for them — yes, you are in the right direction. But it is not there for you — don't worry about it. The tuple mechanism is a forward reference, not examinable material for this course.
Q: Is this minimax approach what old computer chess used? And are the course materials enough for the exam? A: On the first point, yes — the old games used the same principles in a rule-based form. On the exam, we will talk about that in the next session, after we finish the portion — we will spend 10 to 15 minutes on it.
7.4 Static Evaluation Functions
7.4.1 What a Static Evaluation Function Is
Hook. In every minimax example so far, the leaf values were simply given. Who computes them? If a leaf is a terminal state, the utility function scores it exactly — but a partial tree's leaves are usually mid-game positions with no winner yet. The static evaluation function is the machine that turns any position into a number.
In all the minimax examples, the leaf values were given to us. Where do those values come from? That is the job of the static evaluation function. It is pretty simple — very, very simple to compute. And to be explicit: it is not a heuristic. It is a scoring convention that must be defined for each game — it has to be defined per game, and we pre-compute the notion of how we are calculating it. You can call it very similar to your heuristic values or fitness values: prior itself, we determine and compute it, and for each node we compute the value and see.
The vocabulary correction matters here, because a student's instinct is to call it a heuristic:
Q: Is this a heuristic? A: It is not a heuristic. It is a per-game scoring rule: you define it before playing, pre-compute how you calculate it, and compute it for each node. You can call it very similar to your heuristic values or fitness values — but it is a predefined convention, not a heuristic. The distinction in one line: a heuristic is an approximation invented on the fly to estimate something hard; the static evaluation is a fixed, predefined convention you settle before the game — like the agreed price list for chess pieces. It resembles heuristics (it feeds the same search machinery and estimates who is ahead), but it is a convention, not an approximation.
In some games you can just count things: the number of X's and O's; from a position, you can compute which positions are better and how many positions you are away from victory. In richer games you assign point values to pieces.
Formally, a static evaluation takes a position and returns a score estimating how favourable the position is for the player whose side is being evaluated. Most practical evaluation functions are weighted sums of features:
where is the position, is the -th feature of the position (for example "number of white bishops", "number of black queens"), and is the weight that says how important feature is. The chess material count below is exactly this formula with one feature per piece type: = count of that piece, = its point value.
Assumptions & scope. A static evaluation works because we agree it should be correlated with the real chance of winning — but it is only a snapshot: it sees one position, not the moves that follow. It must also be cheap: the whole point is to score many cut-off nodes quickly during search. And it is per-game: the chess material count says nothing about tic-tac-toe, and a backgammon evaluation would need to handle chance. When a position is not "quiet" (for example a piece is about to be captured), a naive static score can be badly wrong — which is why real programs refine the evaluation near tactical positions; this course's scope is the static snapshot itself.
7.4.2 Worked Example: Chess Material Count
Take chess, from heuristic evaluations: if there is a queen on the board, give it 9 points; if there is a bishop, give it 3 points; if there is a pawn (soldier), give it 1 point. (These are the classic chess material values — in full, pawn = 1, knight or bishop = 3, rook = 5, queen = 9 — the lecture uses the queen/bishop/pawn subset.)
The position. Look at a board position. Black has: a queen (9 points), a bishop (3 points), a pawn (1 point) — total . White has only two bishops: .
If we are computing this static evaluation value from the point of view of the white pieces, then:
So the static evaluation equals the utility of white minus the utility of black: the utility of white is 6, the utility of black is 13, and 6 minus 13 gives minus 7. Every symbol is named: (utility) is the material score of a side, is white's total material, is black's total material, and the difference is the position's score from white's point of view — negative means black is ahead.
Final answer: the static evaluation is −7 (from white's viewpoint). At this configuration, who has the better chance of winning? Black — because first, the number of coins is more, and second, black has a queen while white does not. So from white's point of view the value is −7: black is more dominant in that state. The point values (queen 9, bishop 3, pawn 1) are predefined — these are all predefined points per piece.
Sense-check: the formula is symmetric — if we recompute from black's viewpoint we get , exactly the opposite sign, which is what a zero-sum evaluation must satisfy. A score of 0 would mean the material is balanced.
7.4.3 Student Questions and Answers
Q: How do we decide whether black has to be max or min? A: That depends on the starting. When you start the game itself, you will assign that — okay, black is max here and white is min here. You have to take a choice; in either way you can do it. Black can be max or min. The assignment is a design decision made before the search: whoever moves first is usually the root's player, and either role works as long as the utility values are read from that player's point of view consistently.
Q: What about a pawn reaching the last stage and becoming a queen? A: We are just computing what that state means — in that state, who is being more promising or more dominant. Here black is more dominant: there are more coins and they have a queen with them; white is not dominant — white has lost the queen and has only two coins. Based on that, we evaluate; the static evaluation value is −7. Black might win if we go ahead with this option. Promotion changes the pieces on the board (a pawn becomes a queen, +8 material for its side), and the static evaluation is simply recomputed on the new position — the convention itself does not change.
Q: Like points for each piece — are we not compromising in achieving the goal? A: No, we are not — because my goal here is not to win. My goal is so that I don't lose. And what does that also mean? There is a draw rate — I want to ensure that the other person is not winning. This connects straight back to minimax: the algorithm optimizes the guaranteed floor, and the static evaluation feeds that floor. "Not losing" is a well-defined, achievable objective — maximising the worst-case outcome — and a draw is an acceptable resolution.
Exam note: static evaluation values are handed to you in minimax problems — but you must know how they are computed (piece counts, material values, distance-from-victory counts) because next session starts exactly from here: how these values come about, and then alpha-beta pruning. Alpha-beta pruning is the technique that lets minimax skip whole subtrees (like the second and third children of a node whose fate is already decided) without changing the answer — the lecture announced it as the natural next step after static evaluations.
Exam Guidance Summary
Syllabus and scope. The mid-semester syllabus runs until everything covered by the next session: the Introduction to AI module, the Problem Solving Agent using Search module, and this Game Playing module (neural architecture search, adversarial search, game trees, static evaluation, minimax — plus whatever remains next session). Right after the eighth session, the sample papers and the detailed exam syllabus announcement will be posted — give them a glance, but do not take them too seriously: questions are never repeated from past papers, and there is a lot of reverse engineering that can happen when students see sample questions early. The sample papers are intentionally not shared in advance because students get biased by seeing sample questions. The topics get marks proportional to the class time spent on them; the game playing module is examinable for the mid-semester.
Practice material. Every set of course materials has exercises at the end; there is a minimax practice question, and the image captioning problem is the worked practice for CoDeepNEAT — work the first iteration fully yourself (phenotype construction, fitness evaluation, selection, crossover, mutation). The extra numericals are the same style as the exam. Expect the algorithmic search material (A, and variants — vanilla algorithms have variants like IDA, or beam search with restart and best K) and expect a minimax-style computation with static evaluation values: back the values up through a small tree by hand — practice the in-order traversal with ±∞ initialization and the direct min/max trick. For the minimax numericals, the static evaluation values are given in the problem and are precomputed per game.
Course logistics. After game playing, the remaining modules (logic, probability, reasoning over time, ethics) are post-mid-semester. There are no extra classes planned — the course is on track; if needed, the syllabus will be reduced rather than rushed. The quiz policy: the quiz grade is the best of two quizzes, and plagiarism is monitored by the system — several cases were already spotted, so do not do it.
The assignment. The design document should explain your design approach — what the problem statement asks, how you arrived at the algorithm, what data structures you used, why you chose that algorithm over alternatives (for example, why A* and not BFS), screenshots of your outputs, and the performance of your solution. If complexity analysis is asked in the problem statement, do it in one of two ways: asymptotic with Big-O notation, or time it in your Python function over multiple runs and give the average. Implement generically — never hardcode the example; one input file holds one test case; if the graph has multiple equal-cost optimal paths, print both. If the expected output pattern is missing from a problem statement, state your assumption in the design document and provide your own input.txt/output.txt pair in the zip.
How to use this section. For each concept in this lecture, ask: can I work the numerical (minimax tree, roulette-wheel probabilities, image-captioning first iteration) from scratch? The exam favours exactly the numericals that were shown in class — the minimax computation with static evaluation values and the genetic-algorithm first iteration for CoDeepNEAT.
Key Industry Applications
- Automatic architecture design (CoDeepNEAT): deployed for image generation, image classification, speech recognition, and medical analysis. The medical example is the strongest: evolutionary search has produced novel architectures that human doctors and engineers never conceived, because humans are limited by prior knowledge. The original CoDeepNEAT paper itself shipped a working production system — an image-captioning service for a major online magazine, where the evolved network (repeated LSTM modules, summing merges, skip connections) outperformed a hand-tuned baseline on BLEU and CIDEr metrics.
- Modular deep learning: ResNet and GoogLeNet are production examples of repeated, well-organized blocks; CoDeepNEAT's blueprint-module split automates exactly this reuse philosophy. When a module proves itself during evolution, it can be plugged into any blueprint that points to its species — the same way ResNet blocks are repeated dozens of times in production models.
- Microservices-style collaboration: modules evolved by different people or teams can be assembled by someone else at phenotype-construction time — the same division of labor as microservices development. Module development, like service development, is parallelizable across teams; assembly happens against a fixed blueprint contract.
- Image captioning: CNN feature extraction + LSTM text generation + dense+softmax modules, scored by BLEU (bilingual evaluation understudy) — a realistic production pipeline for automatic captions. Beyond captions, the same module repertoire (feature extraction, sequence generator, classifier head) appears in image search, video description, and accessibility tooling for screen readers.
- Game AI history: Deep Blue searching deeper game trees than human grandmasters; early-2000s rule-based computer chess using the same minimax-style principles through rule engines; the Transformers/ChatGPT wave made AI a buzzword, but game-playing AI is decades old. Grandmasters like Pragyananda compensate with pattern knowledge and anticipation, where machines compensate with raw search depth.
- Multi-agent systems: adversarial search underpins modern multi-agent critique architectures, where one agent earns utility for successfully critiquing another. The zero-sum logic of the lecture (my gain is your loss, block the opponent's best response) appears in everything from adversarial training of generative models to multi-agent systems with competing objectives.
- Zero-sum thinking in life: interviews, promotions — situations where one person's gain is another's loss, modeled directly by two-player zero-sum games. The minimax discipline applies to them too: when you cannot guarantee winning, the rational objective is the best guaranteed outcome — ensure the other person is not winning.
ACI Lecture 7 notes · Neural Architecture Search and Game Playing
Sections Breakdown
Neural architecture search as an application of genetic algorithms: NEAT, Deep NEAT and CoDeepNEAT, genotype and phenotype, the full genetic algorithm loop inside CoDeepNEAT, and two worked examples (cat vs dog classification and image captioning).
Search with two or more agents holding conflicting goals: normal versus adversarial search, a game as a formal problem with six components, two-player zero-sum games, and game trees.
Backing values up from leaves to root with max and min nodes, in-order traversal with minus and plus infinity, four fully worked minimax trees, layer ordering, and multiplayer utility tuples.
The per-game scoring convention that gives leaf values to minimax trees: weighted feature sums, the chess material-count example (queen 9, bishop 3, pawn 1), and why it is not a heuristic.
The exam strategy for this lecture: syllabus and scope, practice material (minimax numericals, the CoDeepNEAT image-captioning iteration, search variants), quiz policy, and assignment requirements.
CoDeepNEAT in production (image generation, classification, speech recognition, medical analysis, image captioning), modular deep learning, microservices-style collaboration, game AI history, multi-agent systems, and zero-sum thinking.
Exam Revision Notes
Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.
Neural Architecture Search and Neuroevolution
Must-know: Genotype = blueprint (macro structure, empty-slot nodes with module species IDs) + modules (micro architectures, small neural nets); phenotype = the assembled trainable network built by replacing blueprint placeholders with selected modules. GA steps: initialization (simple random networks), fitness (validation accuracy after limited training), roulette-wheel selection p_i = f_i / sum_j f_j, crossover (aligned by innovation numbers), mutation (add layer, add skip connection, change filter size 3x3 to 5x5), speciation (diversity preservation), generations. Worked captioning iteration: population (M1,M2,M3), (M1,M1,M3) -> N1 = CNN->LSTM->dense, N2 = CNN->CNN->dense; N2 best; one-point crossover; mutation 5x5; species.
⚠️ Top pitfall: Treating the loss as the only error metric: the lecture's simplified L = y_hat - y is a signed error; standard losses are squared error (y_hat - y)^2 or cross-entropy. Also: a module is not one node - each module is a small neural network; and mutation is mandated in the vanilla GA, not optional.
Self-check: In the image captioning problem, why is mutation done even when the crossover child is identical to a parent? (Because the vanilla genetic algorithm always includes mutation - without it the child would be only a crossover of the parents, unlike real evolution.)
Connects to: 7.2 Adversarial Search and Game Playing
Adversarial Search and Game Playing
Must-know: Six components of a formal game: initial state S_0, player function (whose turn), action function (legal moves), result function (new state after an action), terminal test (game over?), utility function (final score). For tic-tac-toe: 9 first moves, 8 replies under each; terminal when a row/column/diagonal completes or the board fills; utility +1 win, 0 draw, -1 loss from max's viewpoint. Zero-sum: one player's gain is the other's loss; Max maximizes the score, Min minimizes Max's score. Win/lose/tie is NOT the result - the result is the state produced by an action.
⚠️ Top pitfall: Confusing outcome with result: 'win, lose, or tie is not the result - the result is the state you get when you apply an action.' Also thinking adversarial search is about the final goal only - the process requires anticipating the opponent's reply before every move.
Self-check: From the initial tic-tac-toe state, how many legal first moves does the max player have, and how many replies does the min player have under each? (Nine first moves; eight replies under each, since O cannot be placed on the existing X.)
Connects to: 7.3 The Minimax Algorithm, 7.4 Static Evaluation Functions
The Minimax Algorithm
Must-know: Back values from leaves to root: max node takes max of children, min node takes min. Example 1: min(4,7)=4, min(2,6)=2, root max(4,2)=4 (left branch). Traversal tree: D=max(-1,8)=8, E=max(-3,-1)=-1, B=min(8,-1)=-1, F=2, G=4, C=min(2,4)=2, A=max(-1,2)=2. Deeper tree: (7,3)->7, (8,-10)->8, (1,8)->8, (9,-3)->9; min(7,8)=7, min(8,9)=8; root 8. Three-children tree: 3,12,8->3; 2,4,6->2; 14,5,2->2; root max(3,2,2)=3. Guarantee: best worst case, never the best outcome; depth does not decide the assurance - values and the algorithm do.
⚠️ Top pitfall: Expecting maximax: 'Max should have gone to the 8/15' - minimax guarantees the best worst case, not the best outcome. Also swapping the infinity roles: max nodes start at -infinity, min nodes at +infinity.
Self-check: In the traversal tree (leaves -1,8 / -3,-1 / 2,1 / -3,4), why does B report -1 instead of 8? (B is a min node; min(8, -1) = -1 - Min refuses the 8 because E's leaves -1 and -3 are worse for Max.)
Connects to: 7.2 Adversarial Search and Game Playing, 7.4 Static Evaluation Functions
Static Evaluation Functions
Must-know: Static evaluation = per-game predefined scoring convention, NOT a heuristic. Chess material: queen 9, bishop 3, pawn 1. Worked position: black = 9+3+1 = 13, white = 3+3 = 6, U = U_white - U_black = 6 - 13 = -7 from white's viewpoint; black is dominant. General form: weighted linear sum EVAL(s) = sum_i w_i f_i(s). Values are handed to you in minimax problems, but know how they are computed; alpha-beta pruning comes next session.
⚠️ Top pitfall: Calling the static evaluation a heuristic - it is a predefined per-game scoring rule. Also forgetting the viewpoint: -7 is from white's side; from black's side the same position is +7.
Self-check: A board has black queen+bishop+pawn and white two bishops: what is the static evaluation from white's point of view? (6 - 13 = -7, black is more dominant.)
Connects to: 7.3 The Minimax Algorithm
Exam Guidance Summary
Must-know: Expect a minimax-style computation with static evaluation values and the algorithmic search material (A*, variants like IDA*, beam search with restart and best K). Work the image captioning problem fully: assume a random initial population, calculate fitness, selection, crossover, mutation - the answer is in the course materials. Topics get marks proportional to class time; game playing is examinable for the mid-semester. Assignment: design document with approach, data structures, algorithm choice rationale, screenshots, performance (Big-O or timed average); implement generically, print both equal-cost optimal paths.
⚠️ Top pitfall: Over-relying on sample questions: questions are never repeated from past papers, and sample papers are withheld because students get biased by seeing them early.
Self-check: If complexity analysis is asked in the assignment problem statement, what two ways are acceptable? (Asymptotic Big-O notation, or timing the Python function over multiple runs and giving the average.)
Connects to: 7.1 Neural Architecture Search and Neuroevolution, 7.2 Adversarial Search and Game Playing, 7.3 The Minimax Algorithm, 7.4 Static Evaluation Functions
Key Industry Applications
Must-know: CoDeepNEAT deployed for image generation, classification, speech recognition, medical analysis (novel architectures humans never conceived). ResNet/GoogLeNet = repeated well-organized blocks; blueprint-module split automates reuse. Microservices-style division of labor at phenotype construction. Image captioning scored by BLEU. Deep Blue searched deeper trees than grandmasters; early-2000s chess used minimax-style rules via rule engines. Multi-agent critique architectures give utility for successful critiques.
⚠️ Top pitfall: Assuming humans exhaust the architecture space - prior knowledge limits exploration, which is exactly why evolutionary search finds architectures experts never thought of (medical example).
Self-check: Why is the microservices analogy apt for CoDeepNEAT? (Different teams evolve different modules, and someone else picks them at phenotype construction - the same division of labor as microservices development.)
Connects to: 7.1 Neural Architecture Search and Neuroevolution, 7.2 Adversarial Search and Game Playing, 7.3 The Minimax Algorithm
Was this lecture useful?
BitsNotes AI Assistant
Subject Notes AssistantConfigure AI Chat
Choose how to access the chatbotSigned in as
Powered by BitsNotes — 20 messages per day. No API key needed. Want unlimited access? Use "Bring Your Own Key" mode.
Sign in to use AI Chat
Get 20 free AI messages per day to ask questions about your lecture notes. Sign in with Google or GitHub — it takes 5 seconds.
Sign In to BitsNotesSwitch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.