I was digging through some code, when suddenly a wild bug appeared...
Ok let's start.
I created this code to represent a position in a script while parsing.
/// <summary>
/// Represents a position in script. (Used for error reporting)
/// </summary>
public sealed class Position
{
/// <summary>
/// Line count.
/// </summary>
public int Line { get; set; }
/// <summary>
/// Character count.
/// </summary>
public int Char { get; set; }
/// <summary>
/// Index data.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Initialize new Position object to standard values.
/// </summary>
public Position()
{
this.Line = 1;
this.Char = 1;
this.Index = 0;
}
/// <summary>
/// Copy a Position object.
/// </summary>
/// <param name="pos"></param>
public Position(Position pos)
{
this.Line = pos.Line;
this.Char = pos.Char;
this.Index = pos.Index;
}
/// <summary>
/// Initialize new Position object to given parameters.
/// </summary>
/// <param name="p_index">The index in stream.</param>
/// <param name="p_line">The line count.</param>
/// <param name="p_char">The character count</param>
public Position(int p_index, int p_line, int p_char)
{
this.Line = p_line;
this.Char = p_char;
this.Index = p_index;
}
/// <summary>
/// Check if 2 Position objects are equal.
/// </summary>
/// <param name="p1">Left operand.</param>
/// <param name="p2">Right operand.</param>
/// <returns>Returns true, if both position objects are equal.</returns>
public static Boolean operator ==(Position p1, Position p2)
{
return
p1.Index == p2.Index &&
p1.Char == p2.Char &&
p1.Line == p2.Line;
}
/// <summary>
/// Check if 2 Position objects are not equal.
/// </summary>
/// <param name="p1">Left operand.</param>
/// <param name="p2">Right operand.</param>
/// <returns></returns>
public static Boolean operator !=(Position p1, Position p2)
{
return !(p1 == p2);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// Equals overload.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj is Position)
return this == (Position)obj;
return false;
}
/// <summary>
/// ToString override.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("{0} - {1} - {2}", this.Index, this.Line, this.Char);
}
}
But... When I do something like this:
var pos1 = new Position();
var pos2 = pos1;
pos2.Char = 10;
Then I change the value of pos1.Char too... I don't know this behavior from C#. The other classes behave the same.
The copy constructor did not help.
I use .NET 4.5 and VS 2012 Update 3...
Can someone tell me what causes this behavior? Or atleast how to get arround this behavior...
pos1 is a reference to the new object. By setting pos2 to pos1, you now have two references to the same object. If you want two different objects, you should do
var pos1 = new Position(); // Create a new object
var pos2 = new Position(pos1); // Use your copy constructor
pos2.Char = 10;
Your ´Position´ class is a ´reference type´
When you equal pos2 to pos1 it points to the same memory location.
Changing the property of one object will thus change the other one also as it is the same object
This isn't a bug; this is correct C# behavior. Position is a class, which is a reference type. The assignment var pos2 = pos1; doesn't copy anything, it just creates another reference to the same Position instance.
You want to call your copy constructor like this:
var pos2 = new Position(pos1);
Related
I'm taking a coursera course where an assignment I'm working on involves completing work on a "take the last rock" style puzzle. For this assignment, the puzzle needs to run with two AI players 600 times at varying levels of AI difficulty. The problem is that after implementing my changes (which involve recursive Minimax algorithms) I'm running into an issue where the unity editor seems to freeze after a few games. It always seems to happen while the newgamedelaytimer is running (timer code provided by the instructor). I've watched it run in debug mode and after a few games the timer stops incrementing and I have to restart Unity.
Here is the code for the base game (where the newgamedelaytimer is called) and the course provided timer.
Is there something I'm missing here?
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
/// <summary>
/// Game manager
/// </summary>
public class DontTakeTheLastTeddy : MonoBehaviour, ITakeTurnInvoker, IGameOverInvoker,
IGameStartingInvoker
{
Board board;
Player player1;
Player player2;
// multiple games support
Timer newGameDelayTimer;
PlayerName firstMovePlayer = PlayerName.Player1;
const int TotalGames = 600;
int gamesPlayed = 0;
// events invoked by class
TakeTurn takeTurnEvent = new TakeTurn();
GameOver gameOverEvent = new GameOver();
GameStarting gameStartingEvent = new GameStarting();
#region Constructor
// Uncomment the code below after copying this class into the console
// app for the automated grader. DON'T uncomment it now; it won't
// compile in a Unity project
/// <summary>
/// Constructor
///
/// Note: The class in the Unity solution doesn't use a constructor;
/// this constructor is to support automated grading
/// </summary>
/// <param name="gameObject">the game object the script is attached to</param>
//public DontTakeTheLastTeddy(GameObject gameObject) :
// base(gameObject)
//{
//}
#endregion
/// <summary>
/// Awake is called before Start
///
/// Leave this method public to support automated grading
/// </summary>
public void Awake()
{
// retrieve board and player references
board = GameObject.FindGameObjectWithTag("Board").GetComponent<Board>();
player1 = GameObject.FindGameObjectWithTag("Player1").GetComponent<Player>();
player2 = GameObject.FindGameObjectWithTag("Player2").GetComponent<Player>();
// register as invoker and listener
EventManager.AddTakeTurnInvoker(this);
EventManager.AddGameOverInvoker(this);
EventManager.AddGameStartingInvoker(this);
EventManager.AddTurnOverListener(HandleTurnOverEvent);
EventManager.AddGameOverListener(HandleGameOverEvent);
// set up timer for delay between games
newGameDelayTimer = gameObject.AddComponent<Timer>();
newGameDelayTimer.Duration = 1.5f;
newGameDelayTimer.AddTimerFinishedListener(HandleTimerFinishedEvent);
// initialize statistics class
Statistics.Initialize();
}
private void HandleGameOverEvent(PlayerName arg0, Difficulty arg1, Difficulty arg2)
{
Debug.Log("Starting New Game Delay timer");
newGameDelayTimer.Run();
//HandleTimerFinishedEvent();
}
/// <summary>
/// Use this for initialization
/// </summary>
void Start()
{
StartGame(PlayerName.Player1, Difficulty.Easy, Difficulty.Easy);
}
/// <summary>
/// Adds the given listener for the TakeTurn event
/// </summary>
/// <param name="listener">listener</param>
public void AddTakeTurnListener(UnityAction<PlayerName, Configuration> listener)
{
takeTurnEvent.AddListener(listener);
}
/// <summary>
/// Adds the given listener for the GameOver event
/// </summary>
/// <param name="listener">listener</param>
public void AddGameOverListener(
UnityAction<PlayerName, Difficulty, Difficulty> listener)
{
gameOverEvent.AddListener(listener);
}
/// <summary>
/// Adds the given listener for the GameStarting event
/// </summary>
/// <param name="listener">listener</param>
public void AddGameStartingListener(UnityAction listener)
{
gameStartingEvent.AddListener(listener);
}
/// <summary>
/// Starts a game with the given player taking the
/// first turn
///
/// Leave this method public to support automated grading
/// </summary>
/// <param name="firstPlayer">player taking first turn</param>
/// <param name="player1Difficulty">difficulty for player 1</param>
/// <param name="player2Difficulty">difficulty for player 2</param>
public void StartGame(PlayerName firstPlayer, Difficulty player1Difficulty,
Difficulty player2Difficulty)
{
// set player difficulties
player1.Difficulty = player1Difficulty;
player2.Difficulty = player2Difficulty;
// create new board
board.CreateNewBoard();
takeTurnEvent.Invoke(firstPlayer,
board.Configuration);
}
/// <summary>
/// Handles the TurnOver event by having the
/// other player take their turn
/// </summary>
/// <param name="player">who finished their turn</param>
/// <param name="newConfiguration">the new board configuration</param>
void HandleTurnOverEvent(PlayerName player,
Configuration newConfiguration)
{
board.Configuration = newConfiguration;
// check for game over
if (newConfiguration.Empty)
{
// fire event with winner
if (player == PlayerName.Player1)
{
gameOverEvent.Invoke(PlayerName.Player2, player1.Difficulty, player2.Difficulty);
}
else
{
gameOverEvent.Invoke(PlayerName.Player1, player1.Difficulty, player2.Difficulty);
}
}
else
{
// game not over, so give other player a turn
if (player == PlayerName.Player1)
{
takeTurnEvent.Invoke(PlayerName.Player2,
newConfiguration);
}
else
{
takeTurnEvent.Invoke(PlayerName.Player1,
newConfiguration);
}
}
}
/// <summary>
/// Starts a new game when the new game delay timer finishes
/// </summary>
void HandleTimerFinishedEvent()
{
Debug.Log("Finished new Game Timer");
// constant provided for autograder support
if (!GameConstants.PlaySingleGame)
{
if (gamesPlayed < TotalGames)
{
if (gamesPlayed % 100 == 0)
{
// uncomment the line below and implement a
// SetPlayerDifficulties method
SetPlayerDifficulties(gamesPlayed);
}
gamesPlayed++;
//Debug.Log("Games Played: " + gamesPlayed);
// alternate player making first move in game
if (firstMovePlayer == PlayerName.Player1)
{
firstMovePlayer = PlayerName.Player2;
}
else
{
firstMovePlayer = PlayerName.Player1;
}
StartGame(firstMovePlayer, player1.Difficulty, player2.Difficulty);
}
else
{
// move to statistics scene when all games have been played
SceneManager.LoadScene("statistics");
}
}
}
private void SetPlayerDifficulties(int gamesPlayed)
{
if (gamesPlayed == 100)
{
player1.Difficulty = Difficulty.Medium;
player2.Difficulty = Difficulty.Medium;
}
else if (gamesPlayed == 200)
{
player1.Difficulty = Difficulty.Hard;
player2.Difficulty = Difficulty.Hard;
}
else if (gamesPlayed == 300)
{
player1.Difficulty = Difficulty.Easy;
player2.Difficulty = Difficulty.Medium;
}
else if (gamesPlayed == 400)
{
player2.Difficulty = Difficulty.Hard;
}
else if (gamesPlayed == 500)
{
player1.Difficulty = Difficulty.Medium;
player2.Difficulty = Difficulty.Hard;
}
}
}
Timer.cs
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// A timer
/// </summary>
public class Timer : MonoBehaviour
{
#region Fields
// timer duration
float totalSeconds = 0;
// timer execution
float elapsedSeconds = 0;
bool running = false;
// support for TimerFinished event
bool started = false;
TimerFinished timerFinished = new TimerFinished();
#endregion
#region Properties
/// <summary>
/// Sets the duration of the timer
/// The duration can only be set if the timer isn't currently running
/// </summary>
/// <value>duration</value>
public float Duration
{
set
{
if (!running)
{
totalSeconds = value;
}
}
}
/// <summary>
/// Gets whether or not the timer has finished running
/// This property returns false if the timer has never been started
/// </summary>
/// <value>true if finished; otherwise, false.</value>
public bool Finished
{
get { return started && !running; }
}
/// <summary>
/// Gets whether or not the timer is currently running
/// </summary>
/// <value>true if running; otherwise, false.</value>
public bool Running
{
get { return running; }
}
/// <summary>
/// Gets ho wmany seconds are left on the timer
/// </summary>
/// <value>seconds left</value>
public float SecondsLeft
{
get
{
if (running)
{
return totalSeconds - elapsedSeconds;
}
else
{
return 0;
}
}
}
#endregion
#region Methods
/// <summary>
/// Update is called once per frame
/// </summary>
void Update()
{
// update timer and check for finished
if (running)
{
elapsedSeconds += Time.fixedDeltaTime;
if (elapsedSeconds >= totalSeconds)
{
running = false;
timerFinished.Invoke();
}
}
}
/// <summary>
/// Runs the timer
/// Because a timer of 0 duration doesn't really make sense,
/// the timer only runs if the total seconds is larger than 0
/// This also makes sure the consumer of the class has actually
/// set the duration to something higher than 0
/// </summary>
public void Run()
{
// only run with valid duration
if (totalSeconds > 0)
{
started = true;
running = true;
elapsedSeconds = 0;
}
}
/// <summary>
/// Stops the timer
/// </summary>
public void Stop()
{
started = false;
running = false;
}
/// <summary>
/// Adds the given number of seconds to the timer
/// </summary>
/// <param name="seconds">time to add</param>
public void AddTime(float seconds)
{
totalSeconds += seconds;
}
/// <summary>
/// Adds the given listener for the TimerFinished event
/// </summary>
/// <param name="listener">listener</param>
public void AddTimerFinishedListener(UnityAction listener)
{
timerFinished.AddListener(listener);
}
#endregion
}
At the current moment I am trying to figure out a way to append a "1" bit to the front of a binary 32 bit integer, but only in front of the active bits. For example, "101" would turn into "1101", and 000 000 would turn into "1 000 000". Each binary integer would essentially be turned into triplets.
The reason I am wanting to do this is to implement a Hashed Octree with morton encoding. I am mainly attempting to follow this article here: https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/
In this article, it references adding a flag "1" bit to the front of morton code with its children in order to distinguish its location code from other location codes.
My Current Code is this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using static Unity.Mathematics.math;
using Unity.Mathematics;
public struct Octree
{
NativeHashMap<int, OctreeNode> octreeNodes;
OctreeNode GetParentNode(OctreeNode node)
{
int locCodeParent = node.LocCode >> 3;
return LookupNode(locCodeParent);
}
OctreeNode LookupNode(int LocCode)
{
OctreeNode node;
if(!octreeNodes.TryGetValue(LocCode, out node))
{
Debug.Log("You done oofed dude, the octree node could not be found with the LocCode provided, please try again or stay oofed");
}
return node;
}
int GetNodeTreeDepth(OctreeNode node)
{
return (int)log2(node.LocCode)/3;
}
}
public struct OctreeNode
{
public int LocCode;
}
The LongFlags class below will maintain a (64-bit) set of ulong bit flags, as an abstraction. You can query the bits that are set, including querying the highest-set bit.
Your code does not show specifics; but you seem to want to be able to add a bit before another already set bit:
"101" would turn into "1101"
and also a not-set bit:
000 000 would turn into "1 000 000"
Perhaps you would keep track of an int value of your highest "Current" bit; and then use this class to set the next bit ... You can set and clear any individual bits.
For example, you could wrap the below LongFlags in a helper like this:
/// <summary>
/// Helper for bit flags, that tracks a <see cref="CurrentBit"/>.
/// The <see cref="BitLimit"/> is limited to sixty-four.
/// Bit positions are numbered from one to sixty-four,
/// right-to-left (lsb is one, msb is 64).
/// </summary>
[DataContract]
public class BitHelper
{
[DataMember]
private LongFlags longFlags = new LongFlags();
/// <summary>
/// Constructor.
/// </summary>
/// <param name="bitLimit">Required: sets <see cref="BitLimit"/>.</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public BitHelper(int bitLimit)
{
if ((bitLimit < 1)
|| (bitLimit > 64))
throw new ArgumentOutOfRangeException(nameof(bitLimit), bitLimit, #"[1, 64]");
BitLimit = bitLimit;
}
/// <summary>
/// Limits the highest bit that can be set. [1, 64].
/// (Bit positions are numbered from one to a maximum of sixty-four,
/// right-to-left (lsb is one, msb is 64).)
/// </summary>
[DataMember]
public int BitLimit { get; private set; }
/// <summary>
/// Identifies the current working bit position.
/// Defaults to one. Ranges: [1, BitLimit].
/// (Bit positions are numbered from one to a maximum of sixty-four,
/// right-to-left (lsb is one, msb is 64).)
/// </summary>
[DataMember]
public int CurrentBit { get; private set; } = 1;
/// <summary>
/// Returns the state of the <see cref="CurrentBit"/>.
/// </summary>
/// <returns>True if one, false if zero.</returns>
public bool IsCurrentBitSet
=> longFlags[CurrentBit];
/// <summary>
/// Increments the <see cref="CurrentBit"/>,
/// making no changes to bit values.
/// </summary>
/// <returns>This object for chaining.</returns>
/// <exception cref="InvalidOperationException">If the <see cref="CurrentBit"/>
/// is already at the <see cref="BitLimit"/>.</exception>
public BitHelper IncrementCurrentBit()
{
if (CurrentBit >= BitLimit)
throw new InvalidOperationException(BitLimit.ToString());
++CurrentBit;
return this;
}
/// <summary>
/// Sets the <see cref="CurrentBit"/> to one.
/// </summary>
/// <returns>This object for chaining.</returns>
public BitHelper SetCurrentBit()
{
longFlags.Set(CurrentBit);
return this;
}
/// <summary>
/// Sets the <see cref="CurrentBit"/> to zero.
/// </summary>
/// <returns>This object for chaining.</returns>
public BitHelper ClearCurrentBit()
{
longFlags.Clear(CurrentBit);
return this;
}
/// <summary>
/// Inverts the value of the <see cref="CurrentBit"/>.
/// </summary>
/// <returns>This object for chaining.</returns>
public BitHelper InvertCurrentBit()
{
longFlags[CurrentBit] = !longFlags[CurrentBit];
return this;
}
/// <summary>
/// Returns the position of the highest bit that is set:
/// [0, BitLimit]. Returns zero if no bits are set.
/// </summary>
public int HighestSetBit
=> longFlags.HighestFlag;
/// <summary>
/// Returns all 64 bits as a ULong.
/// </summary>
/// <returns>All 64 bits, unsigned.</returns>
public ulong ToULong()
=> longFlags.Flags;
/// <summary>
/// Returns all 64 bits as a Long.
/// </summary>
/// <returns>All 64 bits, signed.</returns>
public long ToLong()
=> (long)longFlags.Flags;
/// <summary>
/// Returns the lower 32 bits as a UInt
/// --- REGARDLESS of the setting of <see cref="BitLimit"/>.
/// </summary>
/// <returns>The lower 32 bits, unsigned.</returns>
public uint ToUInt()
=> (uint)longFlags.Flags;
/// <summary>
/// Returns the lower 32 bits as an Int
/// --- REGARDLESS of the setting of <see cref="BitLimit"/>.
/// </summary>
/// <returns>The lower 32 bits, signed.</returns>
public int ToInt()
=> (int)longFlags.Flags;
/// <summary>
/// Returns the lower 16 bits as a UShort
/// --- REGARDLESS of the setting of <see cref="BitLimit"/>.
/// </summary>
/// <returns>The lower 16 bits, unsigned.</returns>
public ushort ToUShort()
=> (ushort)longFlags.Flags;
/// <summary>
/// Returns the lower 16 bits as a short
/// --- REGARDLESS of the setting of <see cref="BitLimit"/>.
/// </summary>
/// <returns>The lower 16 bits, signed.</returns>
public short ToShort()
=> (short)longFlags.Flags;
/// <summary>
/// Returns the lower 8 bits as a Byte
/// --- REGARDLESS of the setting of <see cref="BitLimit"/>.
/// </summary>
/// <returns>The lower 8 bits, unsigned.</returns>
public byte ToByte()
=> (byte)longFlags.Flags;
/// <summary>
/// Returns the lower 8 bits as an SByte
/// --- REGARDLESS of the setting of <see cref="BitLimit"/>.
/// </summary>
/// <returns>The lower 8 bits, signed.</returns>
public sbyte ToSByte()
=> (sbyte)longFlags.Flags;
}
Here is LongFlags:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Sc.Util.System
{
/// <summary>
/// A class holding <see langword="ulong"/> bit <see cref="Flags"/>.
/// Flags are numbered from one to sixty-four, from right-to-left in the
/// <see langword="ulong"/> value --- but the abstraction allows you to
/// ignore that as an implementation detail. This class implements
/// methods to set and clear flags, and set and clear flags from other
/// instances. This also implements implicit conversions to and from
/// <see langword="ulong"/> --- and therefore you may invoke methods with
/// <see langword="ulong"/> values. This is also
/// <see cref="IXmlSerializable"/> and <see cref="ICloneable"/>.
/// </summary>
[Serializable]
public sealed class LongFlags
: ICloneable,
IXmlSerializable
{
/// <summary>
/// Counts the bits that are set on the argument.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <returns>A count of bits that are set.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountBits(ulong flags)
{
int result = 0;
for (int i = 0; i < 64; ++i) {
if ((flags & 1) == 1)
++result;
flags >>= 1;
}
return result;
}
/// <summary>
/// Returns the <see langword="ulong"/> VALUE of the SINGLE
/// highest bit that is set on the argument.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <returns>The value of the single highest bit that is set.
/// Returns zero if no bits are set.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong GetHighestBitValue(ulong flags)
{
int highestBit = LongFlags.GetHighestBitPosition(flags);
return highestBit == 0
? 0UL
: 1UL << (highestBit - 1);
}
/// <summary>
/// Returns the <see langword="ulong"/> VALUE of the SINGLE
/// lowest bit that is set on the argument.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <returns>The value of the single lowest bit that is set.
/// Returns zero if no bits are set.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong GetLowestBitValue(ulong flags)
{
int lowestBit = LongFlags.GetLowestBitPosition(flags);
return lowestBit == 0
? 0UL
: 1UL << (lowestBit - 1);
}
/// <summary>
/// Returns the position of highest bit that is set on the argument:
/// where the right-most bit is position one; and the left-most is sixty-four.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <returns>The position of the highest bit that is set.
/// Returns zero if no bits are set.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetHighestBitPosition(ulong flags)
{
if (flags == 0UL)
return 0;
for (int i = 63; i >= 0; --i) {
if (((flags >> i) & 1) == 1)
return i + 1;
}
Debug.Fail($"Value is '{flags}' but iteration failed to find a set bit.");
return 0;
}
/// <summary>
/// Returns the position of lowest bit that is set on the argument:
/// where the right-most bit is position one; and the left-most is sixty-four.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <returns>The position of the lowest bit that is set.
/// Returns zero if no bits are set.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetLowestBitPosition(ulong flags)
{
if (flags == 0UL)
return 0;
for (int i = 0; i < 64; ++i) {
if (((flags >> i) & 1) == 1)
return i + 1;
}
Debug.Fail($"Value is '{flags}' but iteration failed to find a set bit.");
return 0;
}
/// <summary>
/// Returns a new value from the <paramref name="flags"/>
/// with the <paramref name="predicate"/> removed.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <param name="predicate">Arbitrary.</param>
/// <returns>Returns <paramref name="flags"/> <c>&</c> the
/// complement of the <paramref name="predicate"/>.</returns>
public static ulong Excluding(ulong flags, ulong predicate)
=> flags & ~predicate;
/// <summary>
/// Returns true if the <paramref name="source"/> has ANY of the bits that
/// are set on the <paramref name="flags"/>. Notice that if the
/// <paramref name="flags"/> are zero, this will return false.
/// </summary>
/// <param name="source">The source flags.
/// If zero, this will return false.</param>
/// <param name="flags">Arbitrary bits to search for.
/// If zero, this will return false.</param>
/// <returns>True if ANY <The name="flags"/> are present on the
/// <paramref name="source"/> (and at least one flags bit is set).</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasAnyBits(ulong source, ulong flags)
=> (source & flags) != 0UL;
/// <summary>
/// Returns true if the <paramref name="source"/> has ALL of the bits that
/// are set on the <paramref name="flags"/>. Notice that if the
/// <paramref name="flags"/> are zero, this will return false.
/// </summary>
/// <param name="source">The source flags.
/// If zero, this will return false.</param>
/// <param name="flags">Arbitrary bits to search for.
/// If zero, this will return false.</param>
/// <returns>True if ALL <The name="flags"/> are present on the
/// <paramref name="source"/> (and at least one flags bit is set).</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasAllBits(ulong source, ulong flags)
=> (flags != 0UL) && ((source & flags) == flags);
/// <summary>
/// Returns true if the <paramref name="source"/> has ONLY bits that are set
/// on the <paramref name="flags"/> --- false if any bit is set on the source
/// that is not defined on the flags. Notice that if the
/// <paramref name="flags"/> are zero, this will return false.
/// </summary>
/// <param name="source">The source flags.
/// If zero, this will return false.</param>
/// <param name="flags">Arbitrary bits to search for.
/// If zero, this will return false.</param>
/// <param name="requiresAll">If true, then <paramref name="source"/>
/// MUST contain ALL <paramref name="flags"/> AND NO other bits.
/// If false, the source may contain zero or more bits
/// present on the flags --- and no bits that are not present on the flags
/// (source need not contain all, but can only contain a bit on the flags).</param>
/// <returns>True if only the flags are present on the source --- false if any bit is
/// set on th source that is not defined on the flags.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasOnlyBits(ulong source, ulong flags, bool requiresAll)
=> (flags != 0UL)
&& (source != 0UL)
&& ((source & ~flags) == 0UL)
&& (!requiresAll
|| ((source & flags) == flags));
/// <summary>
/// Returns true if the <paramref name="source"/> has NONE of the
/// bits that are set on <paramref name="flags"/>. Notice that if the
/// <paramref name="flags"/> are zero, this will return TRUE.
/// </summary>
/// <param name="source">The source flags.
/// If zero, this will return true.</param>
/// <param name="flags">Arbitrary flags to search for.
/// If zero, this will return true.</param>
/// <returns>True if no flags bits are set here.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasNoBits(ulong source, ulong flags)
=> (source & flags) == 0UL;
/// <summary>
/// Checks the range.
/// </summary>
/// <param name="position">[1,64].</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void rangeCheckPosition(int position)
{
if ((position <= 0)
|| (position > 64))
throw new ArgumentOutOfRangeException(nameof(position), position, #"[1,64]");
}
/// <summary>
/// Default constructor creates an empty instance.
/// </summary>
public LongFlags() { }
/// <summary>
/// Creates a new instance with each flag position in the argument array set.
/// </summary>
/// <param name="flags">The flags to set.</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public LongFlags(params int[] flags)
=> Set(flags);
/// <summary>
/// Creates a new instance with the given bits set.
/// </summary>
/// <param name="flags">The bits to copy. This directly
/// sets <see cref="Flags"/>.</param>
public LongFlags(ulong flags)
=> Flags = flags;
/// <summary>
/// Creates a deep clone of the argument.
/// </summary>
/// <param name="clone">The value to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private LongFlags(LongFlags clone)
=> Flags = clone.Flags;
XmlSchema IXmlSerializable.GetSchema()
=> null;
void IXmlSerializable.WriteXml(XmlWriter writer)
=> writer.WriteString(Flags.ToString(CultureInfo.InvariantCulture));
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.IsEmptyElement)
Flags = 0UL;
else {
reader.Read();
switch (reader.NodeType) {
case XmlNodeType.EndElement :
Flags = 0UL; // empty after all...
break;
case XmlNodeType.Text :
case XmlNodeType.CDATA :
Flags = ulong.Parse(reader.ReadContentAsString(), CultureInfo.InvariantCulture);
break;
default :
throw new InvalidOperationException("Expected text/cdata");
}
}
}
/// <summary>
/// The current bit flags. Flags are numbered from one to sixty-four: where
/// the right-most bit is one, and the left-most is sixty four.
/// Methods do not require knowledge of the flag positions; and flags
/// are simply numbered [1,64].
/// </summary>
public ulong Flags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private set;
}
/// <summary>
/// An indexer that gets or sets a boolean indicating if the flag at the
/// given <paramref name="position"/> is set.
/// </summary>
/// <param name="position">[1,64].</param>
/// <returns>True if the flag is set.</returns>
public bool this[int position]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsSet(position);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set {
if (value)
Set(position);
else
Clear(position);
}
}
/// <summary>
/// Returns true if the flag at the given position is set.
/// </summary>
/// <param name="position">The position to test: [1,64].</param>
/// <returns>True if the flag is set.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSet(int position)
{
LongFlags.rangeCheckPosition(position);
return LongFlags.HasAnyBits(Flags, 1UL << (position - 1));
}
/// <summary>
/// Returns true if each flag in the argument array is set.
/// This will return FALSE if none are provided.
/// </summary>
/// <param name="positions">[1,64].</param>
/// <returns>True if all provided flags are set. NOTICE: this will
/// return FALSE if none are provided.</returns>
public bool IsAllSet(params int[] positions)
{
if (positions.Length == 0)
return false;
foreach (int position in positions) {
if (!IsSet(position))
return false;
}
return true;
}
/// <summary>
/// Returns true if ANY flag in the argument array is set.
/// This will return FALSE if none are provided.
/// </summary>
/// <param name="positions">[1,64].</param>
/// <returns>True if ANY provided flag is set; AND if AT LEAST ONE
/// is provided.</returns>
public bool IsAnySet(params int[] positions)
{
foreach (int position in positions) {
if (IsSet(position))
return true;
}
return false;
}
/// <summary>
/// Returns true if all flags are set.
/// </summary>
public bool IsFull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Flags == ulong.MaxValue;
}
/// <summary>
/// Returns true if no flags are set.
/// </summary>
public bool IsEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Flags == 0UL;
}
/// <summary>
/// Counts the flags that are set.
/// </summary>
/// <returns>A count of <see cref="Flags"/> bits that are set.</returns>
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => LongFlags.CountBits(Flags);
}
/// <summary>
/// Returns the position of highest flag that is set.
/// </summary>
/// <returns>The position of the highest bit that is set on <see cref="Flags"/>.</returns>
public int HighestFlag
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => LongFlags.GetHighestBitPosition(Flags);
}
/// <summary>
/// Returns the position of lowest flag that is set.
/// </summary>
/// <returns>The position of the lowest bit that is set on <see cref="Flags"/>.</returns>
public int LowestFlag
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => LongFlags.GetLowestBitPosition(Flags);
}
/// <summary>
/// Returns the <see langword="ulong"/> VALUE of the SINGLE
/// highest bit that is set.
/// </summary>
/// <returns>The value of the single highest bit that is set on <see cref="Flags"/>.</returns>
public ulong HighestFlagValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => LongFlags.GetHighestBitValue(Flags);
}
/// <summary>
/// Returns the <see langword="ulong"/> VALUE of the SINGLE
/// lowest bit that is set.
/// </summary>
/// <returns>The value of the single lowest bit that is set on <see cref="Flags"/>.</returns>
public ulong LowestFlagValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => LongFlags.GetLowestBitValue(Flags);
}
/// <summary>
/// Sets the flag at the position specified by the argument.
/// </summary>
/// <param name="position">[1,64].</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags Set(int position)
{
LongFlags.rangeCheckPosition(position);
Flags |= 1UL << (position - 1);
return this;
}
/// <summary>
/// Sets the flag at each specified position.
/// </summary>
/// <param name="positions">[1,64].</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags Set(params int[] positions)
{
ulong flags = Flags;
try {
foreach (int position in positions) {
Set(position);
}
return this;
} catch {
Flags = flags;
throw;
}
}
/// <summary>
/// Sets all flags to one.
/// </summary>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags SetAll()
{
Flags = ulong.MaxValue;
return this;
}
/// <summary>
/// Clears the flag at the position specified by the argument.
/// </summary>
/// <param name="position">[1,64].</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags Clear(int position)
{
LongFlags.rangeCheckPosition(position);
Flags &= ~(1UL << (position - 1));
return this;
}
/// <summary>
/// Clears the flag at each position specified in the argument array.
/// </summary>
/// <param name="positions">[1,64].</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags Clear(params int[] positions)
{
ulong flags = Flags;
try {
foreach (int position in positions) {
Clear(position);
}
return this;
} catch {
Flags = flags;
throw;
}
}
/// <summary>
/// Resets all <see cref="Flags"/> to zero.
/// </summary>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags Clear()
{
Flags = 0UL;
return this;
}
/// <summary>
/// Sets <see cref="Flags"/> to the argument's value.
/// </summary>
/// <param name="clone">The value to copy.</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags SetFrom(LongFlags clone)
{
Flags = clone.Flags;
return this;
}
/// <summary>
/// Sets <see cref="Flags"/> to the argument's value.
/// </summary>
/// <param name="clone">The value to copy.</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags SetFrom(ulong clone)
{
Flags = clone;
return this;
}
/// <summary>
/// Adds all of the flags in the argument to this instance.
/// </summary>
/// <param name="flags">Arbitrary flags to add.</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags AddAllFlags(LongFlags flags)
{
Flags |= flags.Flags;
return this;
}
/// <summary>
/// Removes all flags defined on the argument from this instance.
/// </summary>
/// <param name="flags">Arbitrary flags to remove.</param>
/// <returns>This object for chaining.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags RemoveAllFlags(LongFlags flags)
{
Flags &= ~flags.Flags;
return this;
}
/// <summary>
/// Returns true if this instance has ANY of the <see cref="Flags"/>
/// defined on the argument. Notice that if the
/// <paramref name="flags"/> are zero, this will return false.
/// </summary>
/// <param name="flags">Arbitrary flags to search for.</param>
/// <returns>True if any flags are present here
/// (and at least one flag bit is set).</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAny(LongFlags flags)
=> LongFlags.HasAnyBits(Flags, flags.Flags);
/// <summary>
/// Returns true if this instance has ALL of the <see cref="Flags"/>
/// defined on the argument. Notice that if the
/// <paramref name="flags"/> are zero, this will return false.
/// </summary>
/// <param name="flags">Arbitrary flags to search for.</param>
/// <returns>True if ALL flags are present here
/// (and at least one flag bit is set).</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAll(LongFlags flags)
=> LongFlags.HasAllBits(Flags, flags.Flags);
/// <summary>
/// Returns true if this instance has ONLY flags that are set
/// on the <paramref name="flags"/> --- false if any flag is set here
/// that is not defined on the flags. Notice that if the
/// <paramref name="flags"/> are zero, this will return false
/// --- and if this <see cref="Flags"/> is zero this returns false.
/// </summary>
/// <param name="flags">Arbitrary flags to search for.
/// If zero, this will return false.</param>
/// <param name="requiresAll">If true, then this
/// MUST contain ALL <paramref name="flags"/> AND NO other flags.
/// If false, this may contain zero or more flags
/// present on the flags --- and no flags that are not present on the flags
/// (this need not contain all, but can only contain a flag on the flags).</param>
/// <returns>True if only the flags are present here --- false if any flag is
/// set here that is not defined on the flags.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasOnly(LongFlags flags, bool requiresAll)
=> LongFlags.HasOnlyBits(Flags, flags.Flags, requiresAll);
/// <summary>
/// Returns true if this instance has NONE of the <see cref="Flags"/>
/// defined on the argument.
/// </summary>
/// <param name="flags">Arbitrary.</param>
/// <returns>True if no flags are present here.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasNone(LongFlags flags)
=> LongFlags.HasNoBits(Flags, flags.Flags);
/// <summary>
/// Returns a deep clone of this object.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LongFlags Clone()
=> new LongFlags(this);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
object ICloneable.Clone()
=> Clone();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ulong(LongFlags longFlags)
=> longFlags.Flags;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator LongFlags(ulong flags)
=> new LongFlags(flags);
public override string ToString()
=> $"{nameof(LongFlags)}"
+ "["
+ $"{Convert.ToString((byte)(Flags >> 56), 2)}"
+ $" {Convert.ToString((byte)(Flags >> 48), 2)}"
+ $" {Convert.ToString((byte)(Flags >> 40), 2)}"
+ $" {Convert.ToString((byte)(Flags >> 32), 2)}"
+ $" {Convert.ToString((byte)(Flags >> 24), 2)}"
+ $" {Convert.ToString((byte)(Flags >> 16), 2)}"
+ $" {Convert.ToString((byte)(Flags >> 8), 2)}"
+ $" {Convert.ToString((byte)Flags, 2)}"
+ "]";
}
}
I have a need for a circular buffer (or other data structure), on which I can do a "ToArray" or similar call to get the current state of the buffer and use whilst the buffer carries on possibly overwriting the values held.
The reason for this use case is that the returned data is passed to a worker thread to process and the idea is to ensure that as little data is overwritten as possible in between the calls. The choice of a circular data structure was ostensibly to reduce the memory usage of the buffer to a fixed value.
I built the data structure below, which up until yesterday was sufficient for my needs. The relevant calls are TakeSnapshot, TakeAll and TakeWeakArraySnapshot which are all variations on the same theme.
The call is made quite frequently when about a 1000 samples of reference types are available.
All the calls result in an out of memory exception at some point. The exception is the TakeWeakArraySnapshot which basically reverts to null midway through using the array (something i guess is to do with the fact that the gc handle is weak?)
Is there a more suitable memory efficient data structure for this use or something I am doing wrong? Would changing the gc handle type to normal help. Is it possible to create a output type that wraps the references (like i attempted to with the weakreference) which would be easily be reclaimed by the garbage collector?
Thanks for reading.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Threading;
using System.Runtime.InteropServices;
public class WeakArray<T>
{
private GCHandle[] fArray;
public WeakArray(int length)
{
fArray = new GCHandle[length];
for (int i = 0; i < length; i++)
fArray[i] = GCHandle.Alloc(null, GCHandleType.Weak);
}
~WeakArray()
{
if (fArray == null)
return;
int count = fArray.Length;
for (int i = 0; i < count; i++)
{
GCHandle gcHandle = fArray[i];
if (!gcHandle.IsAllocated)
break;
gcHandle.Free();
}
}
public int Length
{
get
{
return fArray.Length;
}
}
public T this[int index]
{
get
{
return (T) fArray[index].Target;
}
set
{
fArray[index].Target = value;
}
}
}
public class OutputData<TDataType>:IEnumerable<TDataType>,IDisposable
{
private TDataType[] Buffer { get; set; }
private readonly object _lock = new object();
public OutputData(ref TDataType[] buffer,long length)
{
Buffer = buffer;
Length = length;
}
public long Length { get; private set; }
/// <summary>
/// Gets the <see cref="`0"/> at the specified index. Throws IndexOutOfRange for an invalid index
/// or returns the default value of the generic type on an empty queue
/// </summary>
/// <value>
/// The <see cref="`0"/>.
/// </value>
/// <param name="i">The item at that index.</param>
/// <returns></returns>
public TDataType this[int i]
{
get
{
lock (_lock)
{
return Length > 0 ? Buffer[i] : default(TDataType);
}
}
}
public IEnumerator<TDataType> GetEnumerator()
{
for (int i = 0; i < Length; i++)
{
yield return Buffer[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
Array.Clear(Buffer,0,Buffer.Length);
}
public void SetLength(int count)
{
Length = count;
}
}
/// <summary>
/// Implements a CircularBuffer that behaves as a queue
/// </summary>
/// <typeparam name="T"></typeparam>
public class FixedCapacityQueue<T>
{
private T[] _buffer;
private T[] _output;
private OutputData<T> _outputData;
/// <summary>
/// Gets the dropped count. This is the number of samples that have been dropped to
/// maintain the fixed size of the datastructure.
/// </summary>
/// <value>
/// The dropped count.
/// </value>
public int DroppedCount { get; protected set; }
/// <summary>
/// The default number of dropped items required to generate a report
/// </summary>
protected const int DroppedFramesBetweenReports = 1000;
/// <summary>
/// The _start. Index of the first element in buffer.
/// </summary>
private int _start;
/// <summary>
/// The _end. Index after the last element in the buffer.
/// </summary>
private int _end;
/// <summary>
/// The _size. Buffer size.
/// </summary>
private int _numberOfItemsInBuffer;
private readonly object _lock = new object();
/// <summary>
/// Gets or sets the name of the buffer.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; protected set; }
private readonly T _default = default(T);
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityQueue{T}" /> class.
/// </summary>
/// <param name="name">The name of the queue.</param>
/// <param name="bufferSize">Size of the buffer.</param>
public FixedCapacityQueue(string name, int bufferSize)
{
Contract.Requires(bufferSize > 0);
Contract.Requires(!String.IsNullOrEmpty(name));
_buffer = new T[bufferSize];
_output = new T[bufferSize];
_outputData = new OutputData<T>(ref _output, 0);
_start = 0;
_end = _numberOfItemsInBuffer == bufferSize ? 0 : _numberOfItemsInBuffer;
Name = String.Format("FixedCapacityQueue for {0}", name);
}
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityQueue{T}" /> class.
/// </summary>
/// <param name="name">The nameof the buffer.</param>
/// <param name="bufferSize">Size of the buffer.</param>
/// <param name="data">The data to be added to the queue.</param>
/// <exception cref="System.ArgumentException"></exception>
public FixedCapacityQueue(string name, int bufferSize, ICollection<T> data)
: this(name, bufferSize)
{
Contract.Requires(data != null);
Contract.Requires(bufferSize > 0);
Contract.Requires(data.Count < bufferSize);
foreach (var dataItem in data)
{
Enqueue(dataItem);
}
}
/// <summary>
/// Gets a value indicating whether the queue [is empty].
/// </summary>
/// <value>
/// <c>true</c> if [is empty]; otherwise, <c>false</c>.
/// </value>
public bool IsEmpty
{
get
{
lock (_lock)
{
return _numberOfItemsInBuffer == 0;
}
}
}
/// <summary>
/// Gets a value indicating whether the queue [is full].
/// </summary>
/// <value>
/// <c>true</c> if [is full]; otherwise, <c>false</c>.
/// </value>
public bool IsFull
{
get
{
lock (_lock)
{
return Count == Size;
}
}
}
/// <summary>
/// Gets the number of items currently present in the queue.
/// </summary>
/// <value>
/// The count.
/// </value>
public int Count
{
get
{
lock (_lock)
{
return _numberOfItemsInBuffer;
}
}
}
/// <summary>
/// Gets the declared size of the queue.
/// </summary>
/// <value>
/// The size.
/// </value>
public int Size
{
get
{
lock (_lock)
{
return _buffer.Length;
}
}
}
/// <summary>
/// Dequeues an item from the queue. The expected behaviour is that if a Dequeue operation is
/// requested whilst a queue is empty, the default type of the generic queue is returned.
/// </summary>
/// <returns></returns>
public T Dequeue()
{
lock (_lock)
{
if (IsEmpty) return _default;
var item = _buffer[_start];
_buffer[_start] = _default;
Increment(ref _start);
--_numberOfItemsInBuffer;
return item;
}
}
/// <summary>
/// Enqueues the specified item to the queue.
/// </summary>
/// <param name="toAdd">To add.</param>
public void Enqueue(T toAdd)
{
lock (_lock)
{
if (IsFull)
{
_buffer[_end] = toAdd;
Increment(ref _end);
_start = _end;
DroppedCount++;
//report drops
if (DroppedCount >= DroppedFramesBetweenReports)
{
//EventAndErrorPump.Instance.WriteOnce(1000, ReportLevelEnum.Warning,
// String.Format("{0} FixedQueue Dropped Items: {1} ", Name, DroppedCount));
DroppedCount = 0;
}
}
else
{
_buffer[_end] = toAdd;
Increment(ref _end);
++_numberOfItemsInBuffer;
}
}
}
/// <summary>
/// Increments the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Increment(ref int index)
{
if (++index == Size)
{
index = 0;
}
}
/// <summary>
/// Decrements the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Decrement(ref int index)
{
if (index == 0)
{
index = Size;
}
index--;
}
public void Enqueue(IEnumerable<T> toAdd)
{
lock (_lock)
{
foreach (var dataItem in toAdd)
{
Enqueue(dataItem);
}
}
}
public IEnumerator<T> GetEnumerator()
{
var segments = new ArraySegment<T>[2] {ArrayOne(), ArrayTwo()};
foreach (ArraySegment<T> segment in segments)
{
for (int i = 0; i < segment.Count; i++)
{
yield return segment.Array[segment.Offset + i];
}
}
}
/// <summary>
/// Gets the at the specified index. Throws IndexOutOfRange for an invalid index
/// or returns the default value of the generic type on an empty queue. The head/earliest item index can also be
/// null in the event of a dequeue. If the front of the queue is required,please use the front function instead
/// </summary>
/// <param name="i">The item at that index.</param>
/// <returns></returns>
/// <exception cref="System.IndexOutOfRangeException"></exception>
public T this[int index]
{
get
{
lock (_lock)
{
if (IsEmpty)
{
return _default;
}
if (index >= _numberOfItemsInBuffer)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}",
index, _numberOfItemsInBuffer));
}
int actualIndex = InternalIndex(index);
return _buffer[actualIndex];
}
}
}
/// <summary>
/// Converts the index in the argument to an index in <code>_buffer</code>
/// </summary>
/// <returns>
/// The transformed index.
/// </returns>
/// <param name='index'>
/// External index.
/// </param>
private int InternalIndex(int index)
{
return _start + (index < (Size - _start) ? index : index - Size);
}
/// <summary>
/// Clears this instance.
/// </summary>
public void Clear()
{
lock (_lock)
{
_numberOfItemsInBuffer = 0;
_start = 0;
_end = 0;
}
}
/// <summary>
/// Takes a snapshot of the queue and returns it. If used in isolation .i.e not in a buffer implementation
/// it will return all the elements of the queue and leave the queue empty.
/// In a buffer implementation , since the buffer could be accepting data at the point of this call; it is a stale
/// snapshot of the queue when the call is made.
/// </summary>
/// <returns></returns>
[Obsolete("Use TakeAllData() instead")]
public T[] TakeSnapshot()
{
lock (_lock)
{
return TakeSnapshot(Count);
}
}
/// <summary>
/// Takes all data available in the queue.
/// </summary>
/// <returns></returns>
public OutputData<T> TakeAll()
{
var count = Count;
if (count <= 0) return null;
lock (_lock)
{
CopyInto(count, _output);
_outputData.SetLength(count);
return _outputData;
}
}
/// <summary>
/// Takes a snapshot of the queue using the count to limit the output to return. In the event that the specified
/// count is larger than the current size of the queue, it will throw an exception. A zero count value will immediately return
/// In a buffer implementation , since the buffer could be accepting data at the point of this call; it is a stale
/// snapshot of the queue when the call is made.
/// </summary>
/// <returns></returns>
[Obsolete("Use TakeAllData(int count) instead")]
public T[] TakeSnapshot(int count)
{
if (count == 0) return null;
lock (_lock)
{
if (count > _numberOfItemsInBuffer)
{
count = _numberOfItemsInBuffer;
//throw new ArgumentOutOfRangeException(String.Format("Queue size is {0}", Size));
}
var output = new T[count];
CopyInto(count, output);
return output;
}
}
private void CopyInto(int count, T[] output)
{
var lastIndex = (_start + count) - 1;
if (lastIndex >= Size)
{
Array.Copy(_buffer, _start, output, 0, Size - _start);
Array.Copy(_buffer, 0, output, Size - _start, (lastIndex%Size) + 1);
}
else
{
Array.Copy(_buffer, _start, output, 0, count);
}
_start = (_start + count)%Size;
_numberOfItemsInBuffer = _numberOfItemsInBuffer - count;
}
public T[] PeekSnapshot()
{
lock (_lock)
{
var count = Count;
var output = new T[count];
for (var i = 0; i < count; i++)
{
output[i] = this[i];
}
return output;
}
}
public T[] PeekSnapshot(int count)
{
if (count == 0) return null;
lock (_lock)
{
if (count > Size)
{
throw new ArgumentOutOfRangeException(String.Format("Queue size is {0}", Size));
}
var output = new T[count];
for (var i = 0; i < count; i++)
{
output[i] = this[i];
}
return output;
}
}
/// <summary>
/// Gets the front of the queue. The earliest item added to the queue.
/// Use this in lieu of checking this[0] as that could be null whilst items still
/// exist in the queue
/// </summary>
/// <value>
/// The front.
/// </value>
public T Front()
{
lock (_lock)
{
return IsEmpty ? _default : _buffer[_start];
}
}
/// <summary>
/// Gets the utilisation of the datastructure i.e % of the datastructure currently in use
/// </summary>
/// <value>
/// The utilisation.
/// </value>
public float Utilisation
{
get
{
lock (_lock)
{
return CalculateUtilisation();
}
}
}
private float CalculateUtilisation()
{
var util = 0f;
if (Size > 0)
{
util = (Count/(float) Size)*100;
}
return util;
}
/// <summary>
/// Returns the latest item in the queue.
/// </summary>
/// <returns></returns>
public T Back()
{
lock (_lock)
{
return Count > 0 ? _buffer[_end - 1] : _default;
;
}
}
public WeakArray<T> TakeWeakArraySnapshot()
{
lock (_lock)
{
var count = Count;
var arr = new WeakArray<T>(count);
for (var i = 0; i < count; i++)
{
arr[i] = Dequeue();
}
return arr;
}
}
/// <summary>
/// Gets the internal array. Use with EXTREME CAUTION! This is not a thread safe operation
/// and should ONLY be used in situations where any modification to the queue is not possible.
/// Modification operations include Enqueing or Dequeing
/// </summary>
/// <returns></returns>
public T[] GetInternalArray()
{
return _buffer;
}
// doing ArrayOne and ArrayTwo methods returning ArraySegment<T> as seen here:
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1957cccdcb0c4ef7d80a34a990065818d
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1f5081a54afbc2dfc1a7fb20329df7d5b
// should help a lot with the code.
#region Array items easy access.
// The array is composed by at most two non-contiguous segments,
// the next two methods allow easy access to those.
private ArraySegment<T> ArrayOne()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _start, _end - _start);
}
else
{
return new ArraySegment<T>(_buffer, _start, _buffer.Length - _start);
}
}
private ArraySegment<T> ArrayTwo()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _end, 0);
}
else
{
return new ArraySegment<T>(_buffer, 0, _end);
}
}
#endregion
}
Most data structure performance and efficiency comes down to the problem you are trying to solve.
I would recommend not to re-implement data structures yourself unless they don't exist already. Things like ngenerics exist exactly to solve those kind of problems.
This is just a sample project but I'm sure there is other types.
I'm currently working on an exercise for my c# class. I am having some trouble with one particular part and would really appreciate some help.
I am working on an exercise in which we are given an incomplete project file. The project was to create a board game which lets up to six people play (fairly simple in theory). The part I am currently stuck on is showing the player "tokens" on the board.
This is what the final board is meant to look like:
As you can see, the on "0" (start square) there are 6 circles (or tokens) and on the right side there is a datagrid view with columns showing relevant information (colour, name, money, winner).
This is what I have been able to do so far:
As you see, I have been able to show the player name, and money. Though I cannot get the colour to show up in the start square or the data grid view. Instead, I get this:
The two relevant classes are as follows:
Player.CS hold the player object code (constructors etc) (sorry for the wall of text in advance, I am unsure which parts to leave out)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;
namespace SharedGameClasses {
/// <summary>
/// Models a player who is currently located on a particular square
/// with a certain amount of money.
/// </summary>
public class Player {
private const int INITIAL_AMOUNT = 100;
// name of the player
private string name;
public string Name {
get {
return name;
}
set {
name = value;
}
}
// amount of money owned by player
private int money;
public int Money {
get {
return money;
}
set {
money = value;
}
}
// current square that player is on
private Square location;
public Square Location {
get {
return location;
}
set {
location = value;
}
}
// whether the player is a winner, in the current game.
private bool winner;
public bool Winner {
get {
return winner;
}
set {
winner = value;
}
}
// PlayerTokenColour and PlayerTokenImage provide colours for the players' tokens (or "pieces").
private Brush playerTokenColour;
public Brush PlayerTokenColour {
get {
return playerTokenColour;
}
set {
playerTokenColour = value;
playerTokenImage = new Bitmap(1, 1);
using (Graphics g = Graphics.FromImage(PlayerTokenImage)) {
g.FillRectangle(playerTokenColour, 0, 0, 1, 1);
}
}
}
private Image playerTokenImage;
public Image PlayerTokenImage {
get {
return playerTokenImage;
}
}
/// <summary>
/// Parameterless constructor.
/// Do not want the generic default constructor to be used
/// as there is no way to set the player's name.
/// This replaces the compiler's generic default constructor.
/// Pre: none
/// Post: ALWAYS throws an ArgumentException.
/// </summary>
/// <remarks>NOT TO BE USED!</remarks>
public Player() {
throw new ArgumentException("Parameterless constructor invalid.");
} // end Player constructor
/// <summary>
/// Constructor with initialising parameters.
/// Pre: name to be used for this player.
/// Post: this player object has all attributes initialised
/// </summary>
/// <param name="name">Name for this player</param>
public Player(String name, Square initialLocation, Brush playerToken){
Name = name;
location = initialLocation;
Money = INITIAL_AMOUNT;
PlayerTokenColour = playerToken;
//######################### Code needs to be added here ##########################################
} // end Player constructor
/// <summary>
/// Rolls the two dice to determine
/// the number of squares to move forward; and
/// moves the player's location along the board; and
/// obtains the effect of landing on their final square.
/// Pre: dice are initialised
/// Post: the player is moved along the board and the effect
/// of the location the player landed on is applied.
/// </summary>
/// <param name="d1">first die</param>
/// <param name="d2">second die</param>
public void Play(Die d1, Die d2){
var roll1 = d1.Roll();
var roll2 = d2.Roll();
int numofSquares = roll1 + roll2;
Move(numofSquares);
//######################### Code needs to be added here ##########################################
} // end Play.
/// <summary>
/// Moves player the required number of squares forward
/// Pre: the number of squares to move forward
/// Post: the player is moved along the board.
/// NOTE: Refer to Square.cs regarding the NextSquare property.
/// </summary>
/// <param name="numberOfSquares">the number of squares to move</param>
private void Move(int numberOfSquares) {
//######################### Code needs to be added here ##########################################3
} //end Move
/// <summary>
/// Increments the player's money by amount
/// Pre: amount > 0
/// Post: the player's money amount is increased.
/// </summary>
/// <param name="amount">increment amount</param>
public void Credit(int amount) {
Money = Money + amount;
} //end Credit
/// <summary>
/// Decreases the player's money by amount if
/// the player can afford it; otherwise,
/// sets the player's money to 0.
/// Pre: amount > 0
/// Post: player's money is decremented by amount if possible
/// but final amount is not below zero
/// </summary>
/// <param name="amount">decrement amount</param>
public void Debit(int amount){
const int loseamount = 25;
if (Money >= 25){
Money = Money - loseamount;
} else if (Money < 25){
Money = 0;
}
//######################### Code needs to be added here ##########################################3
} //end Debit
} //end class Player
}
And HareandTortoiseGame.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using System.ComponentModel; // for BindingList.
namespace SharedGameClasses {
/// <summary>
/// Plays a game called Hare and the Tortoise
/// </summary>
public static class HareAndTortoiseGame {
// Minimum and maximum players per game
private const int MIN_PLAYERS = 2;
public const int MAX_PLAYERS = 6;
// The dice
private static Die die1 = new Die(), die2 = new Die();
// A BindingList is like an array that can grow and shrink.
//
// Using a BindingList will make it easier to implement the GUI with a DataGridView
private static BindingList<Player> players = new BindingList<Player>();
public static BindingList<Player> Players {
get {
return players;
}
}
private static int numberOfPlayers = 6; // The value 6 is purely to avoid compiler errors.
public static int NumberOfPlayers {
get {
return numberOfPlayers;
}
set {
numberOfPlayers = value;
}
}
// Is the current game finished?
private static bool finished = false;
public static bool Finished {
get {
return finished;
}
}
/// Some default player names.
///
/// These are purely for testing purposes and when initialising the players at the start
///
/// These values are intended to be read-only. I.e. the program code should never update this array.
private static string[] defaultNames = { "One", "Two", "Three", "Four", "Five", "Six" };
// Some colours for the players' tokens (or "pieces").
private static Brush[] playerTokenColours = new Brush[MAX_PLAYERS] { Brushes.Black, Brushes.Red,
Brushes.Gold, Brushes.GreenYellow,
Brushes.Fuchsia, Brushes.White };
/// <summary>
/// Initialises each of the players and adds them to the players BindingList.
/// This method is called only once, when the game first startsfrom HareAndTortoiseForm.
///
/// Pre: none.
/// Post: all the game's players are initialised.
/// </summary>
public static void InitialiseAllThePlayers(){
//Player Playerone = new Player(defaultNames[1], Board.Squares[0]);
int i = 0;
while (i < NumberOfPlayers){
players.Add(new Player(defaultNames[i], Board.Squares[0], playerTokenColours[i]));
i++;
}
//##################### Code needs to be added here. ############################################################
} // end InitialiseAllThePlayers
/// <summary>
/// Puts all the players on the Start square.
/// Pre: none.
/// Post: the game is reset as though it is being played for the first time.
/// </summary>
public static void SetPlayersAtTheStart() {
//##################### Code needs to be added here. ############################################################
} // end SetPlayersAtTheStart
public static void PlayOneRound(){
InitialiseAllThePlayers();
}
} //end class HareAndTortoiseGame
}
Any help/tips will be appreciated, thanks! If you need any more information let me know
EDIT: Additionally, I believe these methods from another class (HareandTortoiseForm.cs) are relevant
/// <summary>
/// Constructor with initialising parameters.
/// Pre: none.
/// Post: the form is initialised, ready for the game to start.
/// </summary>
public HareAndTortoiseForm() {
InitializeComponent();
HareAndTortoiseGame.NumberOfPlayers = HareAndTortoiseGame.MAX_PLAYERS; // Max players, by default.
HareAndTortoiseGame.InitialiseAllThePlayers();
Board.SetUpBoard();
SetupTheGui();
ResetGame();
}
And also ResetGame() which is where I think I am going wrong (i think this where i need to add code)
/// <summary>
/// Resets the game, including putting all the players on the Start square.
/// This requires updating what is displayed in the GUI,
/// as well as resetting the attrtibutes of HareAndTortoiseGame .
/// This method is used by both the Reset button and
/// when a new value is chosen in the Number of Players ComboBox.
/// Pre: none.
/// Post: the form displays the game in the same state as when the program first starts
/// (except that any user names that the player has entered are not reset).
/// </summary>
private void ResetGame() {
// ########################### Code needs to be written ###############################################
}
EDIT 2:
/// <summary>
/// Tells you which SquareControl object is associated with a given square number.
/// Pre: a valid squareNumber is specified; and
/// the boardTableLayoutPanel is properly constructed.
/// Post: the SquareControl object associated with the square number is returned.
/// </summary>
/// <param name="squareNumber">The square number.</param>
/// <returns>Returns the SquareControl object associated with the square number.</returns>
private SquareControl SquareControlAt(int squareNumber) {
int rowNumber;
int columnNumber;
MapSquareNumToScreenRowAndColumn(squareNumber, out rowNumber, out columnNumber);
// Uncomment the following line once you've added the boardTableLayoutPanel to your form.
return (SquareControl) boardTableLayoutPanel.GetControlFromPosition(columnNumber, rowNumber);
// Delete the following line once you've added the boardTableLayoutPanel to your form.
// return null;
} //end SquareControlAt
I can't help you on placing the tokens on DataGridView, but I would rather change the whole board into a TableLayoutPanel. That way you can easily handle each cell separately, and tokens could be just some controls, which are set visible or not.
The Colors should be shown by simply assigning them to the relevant Cells of the DataGridView:
DGV.Rows[playerIndex].Cells[0].Style.BackColor = playerColor;
The Tokens probably ought to be drawn
either on a Bitmap you display in a PictureBox
or directly on a Panel in its Paint event.
Both options are OK. Does this token area change during the game?
Is there an indication in the project that can give you a hint..?
In both cases you will need the Graphics.FillEllipse method.
I'm trying to write a C# algorithm that give the list of possible n-partitions from a pool containing x elements.
In order to be more clear, let's say that I have a pool of 6 elements: 1,2,3,4,5,6 (x = 6)
and I should give all partitions of 3 numbers, ie:
1,2,3 and 4,5,6
1,3,4 and 2,5,6
1,2,5 and 3,4,6
1,2,6 and 3,4,5
etc.
The function I'm targeting looks like :
List<List<List<int>>> GetAllPartitions(int x, int n)
EDIT:
What I'm looking for is not combinations... For it, I use this (taken from this site):
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0
? new[] {new T[0]}
: elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}
Generally I don't do link only answers (and generally SO likes you to show attempts at a solution too), but there is a complete code solution for objects of any type (not just int) already written in c# found here:
http://blogs.msdn.com/b/updownroundnround/archive/2009/12/18/generating-all-partitions-of-a-set-c-implementation.aspx
Note: this will generate all partitions of every length for the set, so you will need to do a simple select on the resulting list to filter it for only your desired "n" length partitions, which can be simply:
var result = from allResults select result where result.length = n || result.length = x-n
For those worried about link death, here is the linked code:
/// <summary>
/// A enumeration of all possible partitions of a set of items.
/// </summary>
/// <typeparam name="T">The type of the items in the set being partitioned.</typeparam>
public sealed class AllPartitionsEnumerable<T> : IEnumerable<IEnumerable<IEnumerable<T>>>
{
/// <summary></summary>
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
private IEnumerable<T> items;
/// <summary>
/// Creates and initializes an instance of the <see cref="AllPartitionsEnumerable{T}"/> type.
/// </summary>
/// <param name="items">The set of items to be partitioned.</param>
public AllPartitionsEnumerable(IEnumerable<T> items)
: base ()
{
this.items = items;
}
/// <summary>
/// Gets an enumerator to iterate over the partitions in this enumeration.
/// </summary>
/// <returns>An instance of <see cref="IEnumerator{T}"/>.</returns>
public IEnumerator<IEnumerable<IEnumerable<T>>> GetEnumerator()
{
return new AllPartitionsEnumerator<T>(this.items);
}
/// <summary>
/// Gets an enumerator to iterate over the partitions in this enumeration.
/// </summary>
/// <returns>An instance of <see cref="IEnumerator{T}"/>.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
/// <summary>
/// An enumerator to iterate over the items in an instance of <see cref="AllPartitionsEnumerable{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the items in the set being partitioned.</typeparam>
public sealed class AllPartitionsEnumerator<T> : IEnumerator<IEnumerable<IEnumerable<T>>>
{
/// <summary>The original set of items over which partitions are created.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private T[] items;
/// <summary>Flag to indicate if this enumerator has been disposed of.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private bool isDisposed;
/// <summary>Flag to indicate if this enumerator is in its initial state.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private bool isFirst;
/// <summary>The number of partitions in the current selection.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int nc;
/// <summary>An array of values indicating the number of values in the partition at the specified index.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int[] p;
/// <summary>An array of indices indicating to which partition the item at the specified index belongs.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int[] q;
/// <summary>The current partition.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private T[][] current;
/// <summary>
/// Creates and initializes an instance of the <see cref="AllPartitionsEnumerator{T}"/> type.
/// </summary>
/// <param name="items">The original set of items over which partitions are enumerated.</param>
public AllPartitionsEnumerator(IEnumerable<T> items)
: base()
{
if (null == items)
{
throw new ArgumentNullException("items");
}
this.isFirst = true;
this.items = items.ToArray();
this.nc = 0;
this.p = new int[this.items.Length];
this.q = new int[this.items.Length];
this.current = null;
}
/// <summary>
/// Gets the current partition.
/// </summary>
public IEnumerable<IEnumerable<T>> Current
{
get
{
this.CheckIfDisposed();
return this.current;
}
}
/// <summary>
/// Disposes of this enumerator and releases all resources held by it.
/// </summary>
public void Dispose()
{
if (this.isDisposed)
{
return;
}
this.isDisposed = true;
this.items = null;
this.p = null;
this.q = null;
this.nc = 0;
this.current = null;
this.isFirst = true;
}
/// <summary>
/// Gets the current partition.
/// </summary>
object IEnumerator.Current
{
get
{
return this.Current;
}
}
/// <summary>
/// Selects the next item in the set of all partitions.
/// </summary>
/// <returns><c>true</c> if an item was selected; <c>false</c> if we are past the last element.</returns>
public bool MoveNext()
{
this.CheckIfDisposed();
if (this.isFirst)
{
this.isFirst = false;
this.nc = 1;
this.p[0] = this.items.Length;
for (int i = 0; i < this.items.Length; ++i)
{
this.q[i] = this.nc;
}
this.Select();
return true;
}
if (this.nc == this.items.Length )
{
return false;
}
int n = this.items.Length;
int m = n;
int l = this.q[m-1];
while (this.p[l - 1] == 1)
{
this.q[m - 1] = 1;
--m;
l = this.q[m - 1];
}
this.nc += m - n;
this.p[0] = this.p[0] + n - m;
if (l == this.nc)
{
++this.nc;
this.p[this.nc - 1] = 0;
}
this.q[m - 1] = l + 1;
this.p[l - 1] = this.p[l - 1] - 1;
this.p[l] = this.p[l] + 1;
this.Select();
return true;
}
/// <summary>
/// Resets this enumerator to its initial state.
/// </summary>
public void Reset()
{
this.CheckIfDisposed();
this.current = null;
this.isFirst = true;
this.isDisposed = false;
this.p = new int[this.items.Length];
this.q = new int[this.items.Length];
this.nc = 0;
}
/// <summary>
/// Selects the items for the current partition.
/// </summary>
private void Select()
{
this.current = new T[this.nc][];
for (int i = 0; i < this.nc; ++i)
{
int k = 0;
this.current[i] = new T[this.p[i]];
for (int j = 0; j < this.items.Length; ++j)
{
if (this.q[j] == i + 1)
{
this.current[i][k] = this.items[j];
++k;
}
}
}
}
/// <summary>
/// Checks and throws an exception if this enumerator has been disposed.
/// </summary>
private void CheckIfDisposed()
{
if (this.isDisposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
}
}
/// <summary>
/// Retrieves all possible partitions of a set of items.
/// </summary>
/// <typeparam name="T">The type of the items in the original set.</typeparam>
/// <param name="items">The original set of items over which partitions are created.</param>
/// <returns>All possible partitions of the items in <paramref name="items"/>.</returns>
public static IEnumerable<IEnumerable<IEnumerable<T>>> AllPartitions<T>(this IEnumerable<T> items)
{
return new AllPartitionsEnumerable<T>(items);
}