-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add Belief, IBelief and Beliefset * docs: add documentation * test: add tests for Belief * test: add tests for Beliefset * fix: fix Reflection bug in Beliefset * fix: make Beliefset constructor protected * docs: change summaries to remarks * fix: adhere to naming conventions for unit tests * chore: change name Beliefset to BeliefSet * chore: change name Beliefset to BeliefSet * fix: change name TResource to TObservation and add more tests * feat: add MemoryBelief skeleton * fix: improve documentation and namings * fix: improve IBelief and Belief summaries * fix: more improvements to docs and tests * chore: correct test comments * chore: rename Beliefset.cs to BeliefSet.cs * chore: rename BeliefsetTests.cs to BeliefSetTests.cs * chore: remove Believe folder * fix: use actual BeliefSet in Goals * feat: add IBeliefSet * chore: rename believe to belief in comments * feat: add circular array datatype * feat: MemoryBelief now uses CircularArray * chore: add missing comments * chore: conform to sonar * fix: make changes according to feedback * fix: GetAllMemories now works as intended * fix: work in some more feedback * fix: change exception type and remove comments * chore: replace resource with obsevation * chore: fix styling and comments * chore: fix spacing in IBeliefSet * chore: add back todo --------- Co-authored-by: Jens Steenmetz <jj.steenmetz@gmail.com> Co-authored-by: Jens Steenmetz <35835627+JensSteenmetz@users.noreply.github.com>
- Loading branch information
1 parent
553533f
commit a9d1765
Showing
5 changed files
with
415 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
using System; | ||
|
||
namespace Aplib.Core.Belief | ||
{ | ||
/// <summary> | ||
/// The <see cref="MemoryBelief{TReference, TObservation}"/> class represents the agent's belief of a single object, | ||
/// but with additional "memory" of previous observations. | ||
/// Some <i>object reference</i> is used to generate/update an <i>observation</i> | ||
/// (i.e., some piece of information on the game state as perceived by an agent). | ||
/// This belief also stores a limited amount of previous observations in memory. | ||
/// </summary> | ||
/// <remarks> | ||
/// It implements the <see cref="IBelief"/> interface. | ||
/// It supports implicit conversion to <typeparamref name="TObservation"/>. | ||
/// </remarks> | ||
/// <typeparam name="TReference">The type of the reference used to generate/update the observation.</typeparam> | ||
/// <typeparam name="TObservation">The type of the observation the belief represents.</typeparam> | ||
public class MemoryBelief<TReference, TObservation> : Belief<TReference, TObservation> | ||
{ | ||
/// <summary> | ||
/// A "memorized" resouce, from the last time the belief was updated. | ||
/// </summary> | ||
private readonly CircularArray<TObservation> _memorizedObservations; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MemoryBelief{TReference, TObservation}"/> class with an object reference, | ||
/// and a function to generate/update the observation using the object reference. | ||
/// Also initializes the memory array with a specified number of slots. | ||
/// </summary> | ||
/// <param name="reference">The reference used to generate/update the observation.</param> | ||
/// <param name="getObservationFromReference">A function that takes a reference and generates/updates a observation.</param> | ||
/// <param name="framesToRemember">The number of frames to remember back.</param> | ||
public MemoryBelief(TReference reference, Func<TReference, TObservation> getObservationFromReference, int framesToRemember) | ||
: base(reference, getObservationFromReference) | ||
{ | ||
_memorizedObservations = new(framesToRemember); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MemoryBelief{TReference, TObservation}"/> class with an object reference, | ||
/// a function to generate/update the observation using the object reference, | ||
/// and a condition on when the observation should be updated. | ||
/// Also initializes the memory array with a specified number of slots. | ||
/// </summary> | ||
/// <param name="reference">The reference used to generate/update the observation.</param> | ||
/// <param name="getObservationFromReference">A function that takes a reference and generates/updates a observation.</param> | ||
/// <param name="framesToRemember">The number of frames to remember back.</param> | ||
/// <param name="shouldUpdate">A function that sets a condition on when the observation should be updated.</param> | ||
public MemoryBelief(TReference reference, Func<TReference, TObservation> getObservationFromReference, int framesToRemember, | ||
Func<bool> shouldUpdate) | ||
: base(reference, getObservationFromReference, shouldUpdate) | ||
{ | ||
_memorizedObservations = new(framesToRemember); | ||
} | ||
|
||
/// <summary> | ||
/// Generates/updates the observation. | ||
/// Also stores the previous observation in memory. | ||
/// </summary> | ||
public override void UpdateBelief() | ||
{ | ||
// We use the implicit conversion to TObservation to store the observation | ||
_memorizedObservations.Put(this); | ||
base.UpdateBelief(); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the most recently memorized observation. | ||
/// </summary> | ||
/// <returns> The most recent memory of the observation.</returns> | ||
public TObservation GetMostRecentMemory() => _memorizedObservations.GetFirst(); | ||
|
||
/// <summary> | ||
/// Gets the memorized observation at a specific index. | ||
/// A higher index means a memory further back in time. | ||
/// If the index is out of bounds, returns the element closest to the index that is in bounds. | ||
/// </summary> | ||
/// <returns> The memory of the observation at the specified index.</returns> | ||
public TObservation GetMemoryAt(int index, bool clamp = false) | ||
{ | ||
int lastMemoryIndex = _memorizedObservations.Length - 1; | ||
if (clamp) | ||
index = Math.Clamp(index, 0, lastMemoryIndex); | ||
else if (index < 0 || index > lastMemoryIndex) | ||
throw new ArgumentOutOfRangeException(nameof(index), $"Index must be between 0 and {lastMemoryIndex}."); | ||
return _memorizedObservations[index]; | ||
} | ||
|
||
/// <summary> | ||
/// Gets all the memorized observations. | ||
/// The first element is the newest memory. | ||
/// </summary> | ||
/// <returns> An array of all the memorized observations.</returns> | ||
public TObservation[] GetAllMemories() | ||
{ | ||
// For now, we return the entire array, but with empty elements for the unused slots | ||
// TODO: make it return only the used slots | ||
return _memorizedObservations.ToArray(); | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
namespace Aplib.Core | ||
{ | ||
/// <summary> | ||
/// An array that wraps around when it reaches its end. | ||
/// Functionally works like a queue with indexing. | ||
/// </summary> | ||
public class CircularArray<T> | ||
{ | ||
/// <summary> | ||
/// The length of the array. | ||
/// </summary> | ||
public int Length { get; private set; } | ||
private readonly T[] _array; | ||
private int _head; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CircularArray{T}"/> class. | ||
/// </summary> | ||
/// <param name="size">The size of the array.</param> | ||
public CircularArray(int size) | ||
{ | ||
Length = size; | ||
_array = new T[Length]; | ||
_head = Length - 1; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CircularArray{T}"/> class. | ||
/// </summary> | ||
/// <param name="array">An array to use as the circular array.</param> | ||
public CircularArray(T[] array) | ||
{ | ||
Length = array.Length; | ||
_array = array; | ||
_head = Length - 1; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the element at the specified index. | ||
/// </summary> | ||
/// <param name="index">The index of the element to get.</param> | ||
/// <returns>The element at the specified index.</returns> | ||
public T this[int index] | ||
{ | ||
get => _array[(index + _head + 1) % Length]; | ||
set => _array[(index + _head + 1) % Length] = value; | ||
} | ||
|
||
/// <summary> | ||
/// Decrements the head of the array. | ||
/// </summary> | ||
private void DecrementHead() | ||
{ | ||
_head = (_head - 1 + Length) % Length; | ||
} | ||
|
||
/// <summary> | ||
/// Puts an element at the start of the array. | ||
/// </summary> | ||
/// <param name="value">The element to add to the array</param> | ||
public void Put(T value) | ||
{ | ||
_array[_head] = value; | ||
DecrementHead(); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the element at the head of the array. | ||
/// </summary> | ||
/// <returns>The element at the head of the array</returns> | ||
public T GetHead() | ||
{ | ||
return _array[_head]; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the first element of the array. | ||
/// </summary> | ||
/// <returns>The last element of the array</returns> | ||
public T GetFirst() | ||
{ | ||
return this[0]; | ||
} | ||
|
||
/// <summary> | ||
/// Converts the circular array to an array. | ||
/// The head should be the last element of the array. | ||
/// Copies from start to end inclusive. | ||
/// </summary> | ||
/// <param name="start">The start index of the range to copy.</param> | ||
/// <param name="end">The end index of the range to copy.</param> | ||
/// <returns>The circular array as a normal array</returns> | ||
public T[] ToArray(int start = 0, int end = -1) | ||
{ | ||
end = end == -1 ? Length - 1 : end; | ||
T[] result = new T[end - start + 1]; | ||
for (int i = 0; i < result.Length; i++) | ||
result[i] = this[start + i]; | ||
|
||
return result; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
using Aplib.Core; | ||
|
||
namespace Aplib.Tests.Core.Belief; | ||
|
||
public class CircularArrayTests | ||
{ | ||
/// <summary> | ||
/// Given a CircularArray instance, | ||
/// When an element is put into the array, | ||
/// The array should wrap around when it reaches its end. | ||
/// (i.e., the first element should become the previous second element, | ||
/// and the last element should be the new element) | ||
/// </summary> | ||
[Fact] | ||
public void Put_ArrayIsFull_WrapsAround() | ||
{ | ||
// Arrange | ||
CircularArray<int> circularArray = new([1, 2, 3, 4, 5]); | ||
|
||
// Act | ||
circularArray.Put(0); | ||
|
||
// Assert | ||
Assert.Equal(0, circularArray[0]); | ||
Assert.Equal(1, circularArray[1]); | ||
Assert.Equal(2, circularArray[2]); | ||
} | ||
|
||
/// <summary> | ||
/// Given a CircularArray instance, | ||
/// When the head is updated, | ||
/// Putting an element should set the correct index | ||
/// even if the head is not at the start of the array. | ||
/// </summary> | ||
[Fact] | ||
public void Put_HeadIsUpdated_SetsCorrectIndex() | ||
{ | ||
// Arrange | ||
CircularArray<int> circularArray = new(3); | ||
|
||
// Act | ||
// circularArray.ToArray() == [0, 0, 0] | ||
circularArray[1] = 6; | ||
// circularArray.ToArray() == [0, 6, 0] | ||
circularArray.Put(4); | ||
// circularArray.ToArray() == [4, 0, 6] | ||
circularArray[1] = 5; | ||
// circularArray.ToArray() == [4, 5, 6] | ||
|
||
// Assert | ||
Assert.Equal(4, circularArray[0]); | ||
Assert.Equal(5, circularArray[1]); | ||
Assert.Equal(6, circularArray[2]); | ||
} | ||
|
||
/// <summary> | ||
/// Given a CircularArray instance, | ||
/// When the head is updated, | ||
/// GetHead should return the correct head. | ||
/// </summary> | ||
[Fact] | ||
public void GetHead_HeadIsUpdated_ReturnsLastElement() | ||
{ | ||
// Arrange | ||
CircularArray<int> circularArray = new([1, 2, 3]); | ||
int prevHead = circularArray.GetHead(); | ||
|
||
// Act | ||
circularArray.Put(0); | ||
int head = circularArray.GetHead(); | ||
|
||
// Assert | ||
Assert.NotEqual(prevHead, head); | ||
Assert.Equal(2, head); | ||
} | ||
|
||
/// <summary> | ||
/// Given a CircularArray instance, | ||
/// When the head is updated, | ||
/// GetLast should return the last element. | ||
/// </summary> | ||
[Fact] | ||
public void GetFirst_HeadIsUpdated_ReturnsFirstElement() | ||
{ | ||
// Arrange | ||
CircularArray<int> circularArray = new([1, 2, 3]); | ||
int prevFirst = circularArray.GetFirst(); | ||
|
||
// Act | ||
circularArray.Put(4); | ||
int firstElement = circularArray.GetFirst(); | ||
|
||
// Assert | ||
Assert.NotEqual(prevFirst, firstElement); | ||
Assert.Equal(4, firstElement); | ||
} | ||
|
||
/// <summary> | ||
/// Given a CircularArray instance, | ||
/// When the head is updated, | ||
/// Converts the circular array to an array in the correct order. | ||
/// </summary> | ||
[Fact] | ||
public void ToArray_HeadIsUpdated_ReturnsCorrectlyOrderedArray() | ||
{ | ||
// Arrange | ||
CircularArray<int> circularArray = new([1, 2, 3, 4, 5]); | ||
|
||
// Act | ||
circularArray.Put(0); | ||
int[] array = circularArray.ToArray(1, 3); | ||
|
||
// Assert | ||
Assert.Equal([1, 2, 3], array); | ||
} | ||
} |
Oops, something went wrong.