/// <summary> /// If we are doing a bidirectional novelty search, flip the creature around and put it in the center of the world. /// </summary> private void beginSecondTrial() { numUpdates++; // Remove the creature from the old region lists // Update the region lists foreach (List <int> region in regions) { region.RemoveAt(indexOfCurrentCreature); } // If the creature has only completed one trial, we need to put it back in the creatureCenter of the world and flip it around. currentCreature.reset(initialHeading - (float)(Math.PI / 2.0)); firstTrial = false; // Add the creature back into the appropriate region lists RotationPacket rp = currentCreature.getRotationPacket(); regions[rp.NWCoord.Y / regionHeight, rp.NWCoord.X / regionWidth].Add(indexOfCurrentCreature); if ((rp.NWCoord.X % regionWidth > rp.SECoord.X % regionWidth) && (rp.NWCoord.Y % regionHeight > rp.SECoord.Y % regionHeight)) { regions[rp.SECoord.Y / regionHeight, rp.SECoord.X / regionWidth].Add(indexOfCurrentCreature); } if (rp.NWCoord.X % regionWidth > rp.SECoord.X % regionWidth) { regions[(rp.NWCoord.Y / regionHeight), rp.SECoord.X / regionWidth].Add(indexOfCurrentCreature); } if (rp.NWCoord.Y % regionHeight > rp.SECoord.Y % regionHeight) { regions[rp.SECoord.Y / regionHeight, rp.SECoord.X / regionWidth].Add(indexOfCurrentCreature); } }
public void SendRotation(Quaternion rotation, uint objectId) { RotationPacket packet = new RotationPacket(); packet.payload = rotation; PacketManager.Instance.SendPacket(packet, objectId, false); }
/// <summary> /// Performs updates to the game engine based on the agent's intended actions. /// </summary> /// <param name="time">GameTime component from the main game engine.</param> public override void Update(GameTime gameTime) { if (!Simulator.paused) { packetBeforeMoving = getRotationPacket(); UpdateMovement(); packetAfterMoving = UpdatePosition(gameTime); checkregions(); Sensor.Update(this, packetAfterMoving); } }
/// <summary> /// Calculate the max possible texture size (bounding box size will change based on angle of rotation). /// </summary> protected void getMaxBoundingBoxSize() { float minX = Position.X; float minY = Position.Y; // Rotate the texture at every possible angle to find the max necessary dimensions for (float theta = (float)-Math.PI; theta < Math.PI; theta += (1.0f / 360.0f)) { rp = getRotationPacketAtAngle(theta); if (rp.NWCoord.X < minX) minX = rp.NWCoord.X; if (rp.NWCoord.Y < minY) minY = rp.NWCoord.Y; } // Calculate the distance from the creature's center to its border (used for calculating sensor placement and dimensions) creatureCenter = new Vector2(Position.X + (Texture.Width / 2), Position.Y + (Texture.Height / 2)); distCenterToBorder = (float)Math.Max(Math.Ceiling(creatureCenter.X - minX), Math.Ceiling(creatureCenter.Y - minY)) + 1.0f; }
/// <summary> /// This method is called when one individual finishes its run in the world (either because it planted itself /// or because it timed out). It should ONLY be called when the creature is being controlled by a neural network (novelty search or evolution). /// </summary> /// private void ResetToFirstTrial() { // Reset numUpdates numUpdates = 0; // Remove the creature from the old region lists // Update the region lists foreach (List <int> region in regions) { region.RemoveAt(indexOfCurrentCreature); } // If the creature has already completed both trials, we need to _actually_ reset the simulator. #region Planter XML writing if (GenomeIndexOfCurrentCreature != -1) { // First, check to see if the individual was a valid planter if (freezeAfterPlanting) { // Success on both trials if (plantedInColoredSpace1 && plantedInColoredSpace2) { numBidirectionalPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_BidirectionalPlanterGenome" + numBidirectionalPlanters.ToString() + ".xml"); } // Success on the first trial else if (plantedInColoredSpace1) { numFirstTrialPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_FirstTrialPlanterGenome" + numFirstTrialPlanters.ToString() + ".xml"); } // Sucess on the second trial else if (plantedInColoredSpace2) { numSecondTrialPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_SecondTrialPlanterGenome" + numSecondTrialPlanters.ToString() + ".xml"); } } else { // Success on both trials, no misplanting if (plantedInColoredSpace1 && plantedInColoredSpace2 && !plantedInWhiteSpace1 && !plantedInWhiteSpace2) { numBidirectionalPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_BidirectionalPlanterGenome" + numBidirectionalPlanters.ToString() + ".xml"); } // Planted only in white // Success on the first trial, no misplanting else if (!plantedInColoredSpace1 && plantedInWhiteSpace1) { numFirstTrialPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_FirstTrialPlanterGenome_White" + numFirstTrialPlanters.ToString() + ".xml"); } // Success on the first trial, no misplanting else if (plantedInColoredSpace1 && !plantedInWhiteSpace1) { numFirstTrialPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_FirstTrialPlanterGenome" + numFirstTrialPlanters.ToString() + ".xml"); } // Sucess on the second trial, no misplanting else if (plantedInColoredSpace2 && !plantedInWhiteSpace2) { numSecondTrialPlanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_SecondTrialPlanterGenome" + numSecondTrialPlanters.ToString() + ".xml"); } // Success on both trials, but with misplanting if ((plantedInColoredSpace1 && plantedInWhiteSpace1) || (plantedInColoredSpace2 && plantedInWhiteSpace2)) { numBidirectionalMisplanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_BidirectionalPlanterGenome_Misplanted" + numBidirectionalMisplanters.ToString() + ".xml"); } // Success on the first trial, but with misplanting else if (plantedInColoredSpace1 && plantedInWhiteSpace1) { numFirstTrialMisplanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_FirstTrialPlanterGenome_Misplanted" + numFirstTrialMisplanters.ToString() + ".xml"); } // Sucess on the second trial, but with misplanting else if (plantedInColoredSpace2 && plantedInWhiteSpace2) { numSecondTrialMisplanters++; XmlDocument results = new XmlDocument(); XmlDeclaration declaration = results.CreateXmlDeclaration("1.0", null, null); results.AppendChild(declaration); XmlGenomeWriterStatic.Write(results, (NeatGenome)ea.Population.GenomeList[GenomeIndexOfCurrentCreature]); results.Save(plantersFolder + "NoveltySearch_SecondTrialPlanterGenome_Misplanted" + numSecondTrialMisplanters.ToString() + ".xml"); } } } #endregion // Remove the individual from the global image list Components.Remove(currentCreature); // Reset the success flags plantedInColoredSpace1 = false; plantedInWhiteSpace1 = false; plantedInColoredSpace2 = false; plantedInWhiteSpace2 = false; // Also reset the trial flag firstTrial = true; // Get the next creature for this generation, decode its CPPN, and replace the creature's controller GenomeIndexOfCurrentCreature++; // Update the folders for storing the archive and planters if necessary if (GenomeIndexOfCurrentCreature % numCreaturesPerFolder == 0) { int newFolderNumber = GenomeIndexOfCurrentCreature / numCreaturesPerFolder; noveltyLogsFolder = Directory.GetCurrentDirectory() + "\\archive\\" + newFolderNumber + "\\"; if (!Directory.Exists(noveltyLogsFolder)) { Directory.CreateDirectory(noveltyLogsFolder); } plantersFolder = Directory.GetCurrentDirectory() + "\\planters\\" + newFolderNumber + "\\"; if (!Directory.Exists(plantersFolder)) { Directory.CreateDirectory(plantersFolder); } } // Initialize the genome's behavior characterization structure if this genome has not previously been evaluated if (ea.Population.GenomeList[GenomeIndexOfCurrentCreature].Behavior == null) { ea.Population.GenomeList[GenomeIndexOfCurrentCreature].Behavior = new BehaviorType(); ea.Population.GenomeList[GenomeIndexOfCurrentCreature].Behavior.behaviorList = new List <double>(); } INetwork newController = controllerSubstrate.generateGenome(ea.Population.GenomeList[GenomeIndexOfCurrentCreature].Decode(ActivationFunctionFactory.GetActivationFunction("BipolarSigmoid"))).Decode(ActivationFunctionFactory.GetActivationFunction("BipolarSigmoid")); if (bidirectionalTrials) { currentCreature = new NNControlledCreature(morphology, initialBoardWidth / 2, initialBoardHeight / 2, initialHeading + (float)(Math.PI / 2.0), newController, this, drawSensorField, trackPlanting, defaultNumSensors, freezeAfterPlanting); } else { currentCreature = new NNControlledCreature(morphology, initialBoardWidth / 2, initialBoardHeight / 2, initialHeading, newController, this, drawSensorField, trackPlanting, defaultNumSensors, freezeAfterPlanting); } // Add the creature back into the appropriate region lists RotationPacket rp = currentCreature.getRotationPacket(); regions[rp.NWCoord.Y / regionHeight, rp.NWCoord.X / regionWidth].Add(indexOfCurrentCreature); if ((rp.NWCoord.X % regionWidth > rp.SECoord.X % regionWidth) && (rp.NWCoord.Y % regionHeight > rp.SECoord.Y % regionHeight)) { regions[rp.SECoord.Y / regionHeight, rp.SECoord.X / regionWidth].Add(indexOfCurrentCreature); } if (rp.NWCoord.X % regionWidth > rp.SECoord.X % regionWidth) { regions[(rp.NWCoord.Y / regionHeight), rp.SECoord.X / regionWidth].Add(indexOfCurrentCreature); } if (rp.NWCoord.Y % regionHeight > rp.SECoord.Y % regionHeight) { regions[rp.SECoord.Y / regionHeight, rp.SECoord.X / regionWidth].Add(indexOfCurrentCreature); } }
/// <summary> /// During each call to Sense(), this function is called to update the list of images that intersect the sensor field. /// p1 is the NW sensor field corner, and p2 is the SE sensor field corner. /// </summary> private List <Image> updateIntersectingImages(Image creature, RotationPacket creaturePacket) { // Figure out which regions the sensor field could intersect List <int> indicesOfPossibleNeighbors = new List <int>(); indicesOfPossibleNeighbors.AddRange(Simulator.regions[creaturePacket.NWCoord.Y / Simulator.regionHeight, creaturePacket.NWCoord.X / Simulator.regionWidth]); indicesOfPossibleNeighbors.AddRange(Simulator.regions[creaturePacket.NWCoord.Y / Simulator.regionHeight, creaturePacket.SECoord.X / Simulator.regionWidth]); indicesOfPossibleNeighbors.AddRange(Simulator.regions[creaturePacket.SECoord.Y / Simulator.regionHeight, creaturePacket.NWCoord.X / Simulator.regionWidth]); indicesOfPossibleNeighbors.AddRange(Simulator.regions[creaturePacket.SECoord.Y / Simulator.regionHeight, creaturePacket.SECoord.X / Simulator.regionWidth]); // Grab the images corresponding to the indices for the possible neighbors float imageDepth; Image candidateImage; SortedDictionary <float, Image> neighboringRegions = new SortedDictionary <float, Image>(); foreach (int index in indicesOfPossibleNeighbors) { candidateImage = (Image)creature.Game.Components[index]; imageDepth = candidateImage.Depth; if (!neighboringRegions.ContainsKey(imageDepth) && candidateImage != creature && imageDepth > creature.Depth) { neighboringRegions.Add(imageDepth, candidateImage); } } // Go through those regions and find the images that intersect the sensor field // (Also, we only want images that are underneath the sensor field) int x1, x2, y1, y2; SortedDictionary <float, Image> intersectingImages = new SortedDictionary <float, Image>(); foreach (RotateableImage image in neighboringRegions.Values.OfType <RotateableImage>()) { RotationPacket imagePacket = image.getRotationPacket(); int halfChangeInWidth = (imagePacket.NewWidth - image.Texture.Width) / 2; int halfChangeInHeight = (imagePacket.NewHeight - image.Texture.Height) / 2; // (x1, y1) is the NE corner of the intersection space x1 = Math.Max(creaturePacket.NWCoord.X, imagePacket.NWCoord.X); y1 = Math.Max(creaturePacket.NWCoord.Y, imagePacket.NWCoord.Y); // (x2, y2) is the SW corner of the intersection space x2 = Math.Min(creaturePacket.SECoord.X, imagePacket.SECoord.X); y2 = Math.Min(creaturePacket.SECoord.Y, imagePacket.SECoord.X); // If there is a nonzero intersection space and the image under consideration isn't already in the image list, // add it to the image list. if (x1 < x2 && y1 < y2) { intersectingImages.Add(image.Depth, image); } } // Take care of the non-mobile images (basically just region backgroudnds) foreach (Image image in neighboringRegions.Values.OfType <StaticImage>()) { // (x1, y1) is the NE corner of the intersection space x1 = Math.Max(creaturePacket.NWCoord.X, Convert.ToInt32(Math.Floor(image.Position.X))); y1 = Math.Max(creaturePacket.NWCoord.Y, Convert.ToInt32(Math.Floor(image.Position.Y))); // (x2, y2) is the SW corner of the intersection space x2 = Math.Min(creaturePacket.SECoord.X, Convert.ToInt32(Math.Floor(image.Position.X)) + image.Texture.Width); y2 = Math.Min(creaturePacket.SECoord.Y, Convert.ToInt32(Math.Floor(image.Position.Y)) + image.Texture.Height); // If there is a nonzero intersection space and the image under consideration isn't already in the image list, // add it to the image list. if (x1 < x2 && y1 < y2) { intersectingImages.Add(image.Depth, image); } } // Convert the dictionary to a list and return it return(intersectingImages.Values.ToList()); }
/// <summary> /// Returns the color contents of the area defined by the sensor field. This output will feed into the neural controller inputs. /// The neural controller has 3 input layers, which are arranged on the same horizontal level. The current version of this function /// assumes the sensor field is a square so that the sensor data can meaningfully be stored in a 2D array. A better version of this /// function will be made later so that sensor fields with different shapes are supported. /// </summary> /// <param name="creaturePosition">The absolute position of the creature in the world, measured from the upper left corner.</param> public void Update(Creature creature, RotationPacket creaturePacket) { // (These two variables don't factor into the actual sensing, but they are necessary if we want to visualize the sensor contents.) Color[] sensorContentsPixels = new Color[Simulator.FieldTexture.Width * Simulator.FieldTexture.Height]; for (int row = 0; row < Simulator.FieldTexture.Height; row++) { for (int col = 0; col < Simulator.FieldTexture.Width; col++) { sensorContentsPixels[col + (row * Simulator.FieldTexture.Width)] = Color.Transparent; } } // Reset the sensors resetArrays(); // Find the new bounding box coordinates using the new creatureCenter List <Image> intersectingImages; if (Simulator.blindCreatures) { intersectingImages = new List <Image>(); intersectingImages.Add(Simulator.initialBackground); } else if (Simulator.everyoneCanPlant) { intersectingImages = new List <Image>(); intersectingImages.Add(Simulator.backgroundImage); } else { intersectingImages = updateIntersectingImages(creature, creaturePacket); } // Grab the rotation packets for each of the images in intersectingImages List <RotationPacket> rotationPackets = new List <RotationPacket>(); for (int i = 0; i < intersectingImages.Count; i++) { rotationPackets.Add(intersectingImages[i].getRotationPacketWithoutSensors(true)); } // Find the global coordinates of the creature's position and the sensor field's NW coordinate, // along with the local coordinates of the creature's center Point creaturePosition = VectorHelpers.asXNAPoint(creature.Position); Point sensorNWCoord = new Point(creaturePosition.X + creature.SensorFieldAnchorPoint.X - (SegmentLength * ResolutionX) / 2, creaturePosition.Y + creature.SensorFieldAnchorPoint.Y - (SegmentLength * ResolutionY) / 2); Vector2 creatureCenter = new Vector2(creature.Texture.Width / 2, creature.Texture.Height / 2); // Iterate through the sensor field texel positions and query for non-transparent pixels underneath the field Color worldTexel; int indexIntoArray; for (int x = 0; x < SegmentLength; x++) { for (int y = 0; y < SegmentLength; y++) { // Get the global position that corresponds to the internal (x,y) coordinates Point globalSensorCoord = MathHelpers.rotateAroundPoint(sensorNWCoord.X + SensorArray[x, y].X, sensorNWCoord.Y + SensorArray[x, y].Y, Convert.ToInt32(creature.Position.X + creatureCenter.X), Convert.ToInt32(creature.Position.Y + creatureCenter.Y), creature.XNAHeading); // Loop through each image, starting with the one that's closest to (yet still underneath) the sensor field for (int i = 0; i < intersectingImages.Count; i++) { // Check to make sure the image actually has a pixel located at the (global) x,y we're checking Point imageNW = rotationPackets[i].NWCoord; Point imageSE = rotationPackets[i].SECoord; if (globalSensorCoord.X >= imageNW.X && globalSensorCoord.X < imageSE.X && globalSensorCoord.Y >= imageNW.Y && globalSensorCoord.Y < imageSE.Y) { indexIntoArray = (globalSensorCoord.X - imageNW.X + ((globalSensorCoord.Y - imageNW.Y) * rotationPackets[i].NewWidth)); worldTexel = rotationPackets[i].RotatedPixels[indexIntoArray]; if (worldTexel.A != 0) { // If we get here, the sensor field is working as it should! sensedSomething = true; // Store the sensed RGB values in an ANN-friendly format R[x, y] = MathHelpers.Scale(worldTexel.R, 0, 255, -1, 1); G[x, y] = MathHelpers.Scale(worldTexel.G, 0, 255, -1, 1); B[x, y] = MathHelpers.Scale(worldTexel.B, 0, 255, -1, 1); // If we're visualizing the sensor field contents, we need to update the texture-to-be-drawn also if (Simulator.drawSensorField && (!Simulator.depthTest || creature.ID == Simulator.manuallyControlledCreatureID)) { sensorContentsPixels[x + ((y * ResolutionX) * SegmentLength)] = worldTexel; } // Then stop looping through the images and move on to the next texel location break; } } } } } // If we want to visualize the sensor field, we have to do it before we dispose of the temporary texture if (Simulator.drawSensorField) { Simulator.sensorContentsTexture.SetData(sensorContentsPixels); } // Raise an exception if the sensors are broken if (!sensedSomething) { throw new Exception("Problem: Sensor.Sense() looped through the pixels in rotatedSensorField without finding any pixels with a nonzero alpha value."); } // Reset the exception flag sensedSomething = false; }