C# increment decrement List values - c#

So sorry for this vague question... its doing my head in...
Basically just a test project, use the [+] key to add a bunch of values into the List.... The arrow keys need to go up and down the List values...
When a new value is added to the list the "historySelected" is reset, so that the 'Up Arrow'.. always selects the last item added in the List first, and then if you keep pressing it, it goes back through the List values. At anytime if the 'Down Arrow' key is pressed it needs to go back to the previous value in the List of values. At no point should a Up Arrow key followed by a Down Arrow key show the same List value, and vice versa..
The behavior I'm looking for is much like a developer console with a command history, where Up arrow goes further back in history, and Down arrow the opposite.
Again apologies for the code, its been through so many test changes, currently this works going Up arrow to the start of the List value, and then using the Down arrow to go all the back to the highest List index... but the problem is if you go half way through the List (or inbetween anyway), and switch from Up arrow.. to Down arrow.. the values increment/decrement don't act accordingly... I can't seem to get the right codelogic for it.
Any help or suggestions thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ArrowKeys {
class Program {
//public static List<string> cmdHistory = new List<string>() { "1", "2", "3", "4", "5" };
public static List<string> cmdHistory = new List<string>();
public static int cmdHistoryCount = 0;
public static int historySelected = 0;
public static int num = 0;
public static string current = "";
public enum UpDown {
reset,
up,
down
};
public static UpDown LastUpDown;
static void Main(string[] args) {
bool running = true;
while (running) {
ConsoleKeyInfo kb = Console.ReadKey();
switch (kb.Key) {
case ConsoleKey.Escape:
Console.WriteLine("Exit");
running = false;
return;
case ConsoleKey.Add:
cmdHistory.Add(num.ToString());
cmdHistoryCount++;
historySelected= cmdHistoryCount;
LastUpDown = UpDown.reset;
Console.WriteLine(string.Join(" ", cmdHistory.Cast<string>().ToArray()));
num++;
break;
case ConsoleKey.UpArrow:
UpArrow();
LastUpDown = UpDown.up;
Console.WriteLine("UpArrow pressed");
Console.WriteLine(current);
break;
case ConsoleKey.DownArrow:
DownArrow();
LastUpDown = UpDown.down;
Console.WriteLine("DownArrow pressed");
Console.WriteLine(current);
break;
}
}
}
public static int Clamp(int value, int min, int max) {
return (value < min) ? min : (value > max) ? max : value;
}
public static void UpArrow() {
if (cmdHistoryCount != 0) {
if (LastUpDown == UpDown.down) {
if (historySelected - 1 == 0) {
return;
}
}
current = cmdHistory[historySelected - 1];
if (historySelected - 1 == 0) {
return;
}
historySelected--;
}
}
public static void DownArrow() {
if (cmdHistoryCount != 0) {
if (LastUpDown == UpDown.up) {
if (historySelected + 1 > cmdHistoryCount) {
return;
}
}
current = cmdHistory[historySelected - 1];
if (historySelected + 1 > cmdHistoryCount) {
return;
}
historySelected++;
}
}
}
}

It sounds like you simply need to stop your index going out of range. It's not clear what the existing code was intended to do, but your clarification seems to imply it's not necessary. Try this:
public static void UpArrow()
{
historySelected = Math.Max(0, historySelected - 1);
current = cmdHistory[historySelected];
}
public static void DownArrow()
{
var maxIndex = cmdHistory.Count - 1;
historySelected = Math.Min(maxIndex, historySelected + 1);
current = cmdHistory[historySelected];
}

If I'm understanding what you're looking for correctly, a pair of dueling stacks might work for your case. They're going to give you the FILO/LIFO style item access you're looking for. Drop the List and do something like:
var UpStack = new Stack<string>();
var DownStack = new Stack<string>();
On "new item"
UpStack.Push(newItem);
On "Up"
DownStack.Push(UpStack.Pop());
On "Down"
UpStack.Push(DownStack.Pop());
If you wanted a "buffer" item that isn't in either of the stacks, that's easy enough to do by just Popping into the buffer item. It's a little more management, because you'd have to push it onto one stack, then pop the other each time, but it's whatever. There's also the Peek() method, which gets the item on top of a Stack without Popping it, so you use that for your display item, as well.

Related

C# repeating Enum elements

thanks in advance. I'm a Unity developer.
So I have my AI states in enum converted to a list.
public enum IdleState
{
BasicIdle,
PlayingWithKids,
Playfull,
Curious,
Bored,
MoveToCamera,
Waiting,
PlantMode,
Shy,
Crying
}
public List<IdleState> availableIdleStates = new List<IdleState>()
{
IdleState.BasicIdle,
IdleState.PlayingWithKids,
IdleState.Playfull,
IdleState.Curious,
IdleState.Bored,
IdleState.Waiting,
IdleState.PlantMode,
IdleState.Shy,
IdleState.Crying
};
I'm controlling the order of execution through my public list in my inspector witch works fine, however I would like to have basic idle (enum int = 0) execute multiple times in my inspector order of execution. Although when the list gets to my second basic idle it restarts and gets stuck between the two basic idles I assume its because its value in enum is 0, but i really don't know whats causing this, any help would be a blessing. thanks guys!
private void FixedUpdate()
{
if (Time.timeSinceLevelLoad > prevIdleStateChangeTime + currentStateDuration)
{
int i = availableIdleStates.FindIndex(x => x.Equals(currentIdleState))+1;
if (i >= availableIdleStates.Count)
{
i = 0;
}
changeState(availableIdleStates[i]);
}
switch (currentIdleState)
{
case IdleState.BasicIdle:
if (Time.timeSinceLevelLoad > subStateChangeTime + subStateDuration)
{
subStateChangeTime = Time.timeSinceLevelLoad;
switch (randInt)
{
//something
}
}
break;
case IdleState.PlayingWithKids:
if (Time.timeSinceLevelLoad > subStateChangeTime + subStateDuration)
{
int randInt = Random.Range(0, 3);
subStateChangeTime = Time.timeSinceLevelLoad;
switch (randInt)
{
//something
}
}
break;
etc..
every state change this method is being called:
private void changeState(IdleState NewState)
{
currentIdleState = NewState;
prevIdleStateChangeTime = Time.timeSinceLevelLoad;
mover.resetParameters();
subStateChangeTime = Time.timeSinceLevelLoad;
subStateDuration = -1;
}
availableIdleStates.FindIndex(x => x.Equals(currentIdleState))
Here. If first and second object is the same this will always find first occurence of your duplicated state (id = 0), your script changes it to id = 1 but in next update again first element is found and your id is back to 0. You should have field for that int value instead of finding index on list if you want to have duplicate elements.

Execute methods following the order of a list in C#

I have a unity project and want to call methods one after another using a predefined list.
In this test scenario I want to execute startTrialWithFixedValue1() two times, startTrialWithFixedValue2() two times and startTrialWithRandomValue() one time in the end. Every time the user makes an input, I want to execute the next method from the list. There are two major problems with my code:
(1) Whenever I use an if statement with return the variable countTrial becomes unreachable. The debug log shows always 0:1 and the first method is executed all the time.
(2) If I exclude the return statement, all methods are started at once.
[EDIT] To specify my question: How can I execute methods following the order of a given list.
Script A:
public int countTrial;
public int maxTrials = 16;
public Spawner spawner;
List<int> trialMarkers = new List<int>() {1, 1, 2, 2, 3};
public void chooseNextTrial()
{
for (int countTrial = 0; countTrial < maxTrials; countTrial++)
{
Debug.Log(countTrial + ": " + trialMarkers[countTrial]);
if (trialMarkers[countTrial] == 1)
{
spawner.startTrialWithFixedValue1();
return;
}
if (trialMarkers[countTrial] == 2)
{
spawner.startTrialWithFixedValue2();
return;
}
else
{
spawner.startTrialWithRandomValue();
return;
}
}
Script B:
public void handleUserInput()
{
if (Input.GetButtonDown("Fire3"))
{
deleteAllChildren();
chooseTrial.chooseNextTrial();
}
}
}
Hope you guys can help me!
[EDIT2] As suggested by Programmer: Remove the return statement in that function then run it again. Copy and paste the result of Debug.Log(countTrial + ": " + trialMarkers[countTrial]); the result is always:
5:3
5:3
5:3
...
You need to remove the For loop and handle the execution of Method more manualy
public int countTrial = 0;
public int maxTrials = 16;
public Spawner spawner;
List<int> trialMarkers = new List<int>() {1, 1, 2, 2, 3};
public void chooseNextTrial()
{
Debug.Log(countTrial + ": " + trialMarkers[countTrial]);
if (trialMarkers[countTrial] == 1)
{
spawner.startTrialWithFixedValue1();
}
if (trialMarkers[countTrial] == 2)
{
spawner.startTrialWithFixedValue2();
}
else
{
spawner.startTrialWithRandomValue();
}
}
And then inside your second script (Script B) which handles the user input
public void handleUserInput()
{
if (Input.GetButtonDown("Fire3"))
{
deleteAllChildren();
chooseTrial.chooseNextTrial();
chooseTrial.countTrial++;
}
}
End line: You shouldnt use a Fop loop since you want the sequence to be startied whith a player input action and handle it like this. Also adding a method like this:
public void NextCounter()
{
if(countTrial<trialMarkers.Count)
countTrial++
}
In your Script A and then call it like this in Script B to avoid your counter going above the list count.
chooseTrial.NextCounter();
Insteed of using the countTrial++ in SCript B
When you return in the method you are breaking out of your loop.
for (int countTrial = 0; countTrial < (maxTrials <= trialMarkers.Count ? maxTrails : trialMarkers.Count) ; countTrial++)
{
Debug.Log(countTrial + ": " + trialMarkers[countTrial]);
if (trialMarkers[countTrial] == 1)
{
spawner.startTrialWithFixedValue1();
}
if (trialMarkers[countTrial] == 2)
{
spawner.startTrialWithFixedValue2();
}
else
{
spawner.startTrialWithRandomValue();
}
}
Will execute the way you are asking. Also note I changed your iterator so that you do not get an object reference error in your loop.
How about doing something similar to what you describe in the title (which gives the impression that you have a "list of functions"). This example is easily extensible:
private Action[] _functions;
public void MainEntryPoint()
{
_functions = new Action[] { StartTrialWithFixedValue1, StartTrialWithFixedValue2, StartTrialWithRandomValue };
List<int> trialMarkers = new List<int>() { 1, 1, 2, 2, 3 };
DoThings(trialMarkers);
}
public void DoThings(IEnumerable<int> indexesOfFuctions)
{
foreach (var index in indexesOfFuctions)
{
_functions[index-1]();
}
}
private void StartTrialWithFixedValue1()
{
Trace.WriteLine("StartTrialWithFixedValue1");
}
private void StartTrialWithFixedValue2()
{
Trace.WriteLine("StartTrialWithFixedValue2");
}
private void StartTrialWithRandomValue()
{
Trace.WriteLine("StartTrialWithRandomValue");
}
I create an "array of functions" (actually of Delegates to functions), and then I use your list of instructions to execute them.

Check each string value in dictionary for specific characters c#

I am trying to read through a dictionary and get all the values in it then check if the value in the dictionary location is equal to (A) when a button is clicked
if the value in the dictionary ends with (A) then the word "correct" is printed , else "incorrect is printed"
but that happens when i click the button is that the correctness and in-correctness of each value is printed. so it does identify the correct answer and the incorrect answer but i only need it to print out either correct of incorrect
the issue occurs in the OnGUI function.
the code allows for a random key which are questions to be selected on button click and its values which are answers displayed as buttons , you then click a button . correct answers are the values that end with (A) . bu clicking the buttons which are displayed with the value name you will check if you have selected the correct answer but there is a slight problem with it . it prints out the correctness and in-correctness of each value in the selected key
here is the entire code
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class testQuestions : MonoBehaviour {
Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>();
string []vl ;
string ky;
int Qnum;
string A;
int indx;
// Use this for initialization
void Start () {
dictionary.Add("ups", new string[] {"updegree", "popup (A)"});
dictionary.Add("down aroud the place like this ", new string[] {"sun", "bun(A)","art"});
dictionary.Add("left", new string[] {"higi (A)", "migi"});
dictionary.Add("right", new string[] {"een", "yyo(A)"});
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown("q"))
{
GenerateRandoms();
}
}
void GenerateRandoms()
{
string[] keys = new string[dictionary.Count];//get the dictionary count and store in array
dictionary.Keys.CopyTo(keys, 0);
var index = Random.Range(0, keys.Length);
var key = keys[index];
var value = dictionary[key];
ky= key;
vl = value;
foreach (string ku in value)
{
// create buttons with the ku text as the button text
indx = index;
Qnum +=1;
A = ku;
}
//---------- remove each after answer button click
//dictionary.Remove(key); // remove key after it is already displayed. this must only be exicuted at the end of the function
}
void OnGUI ()
{
int charlength = 0;
foreach(char NumOfChar in ky)
{
charlength ++;
}
GUI.TextArea(new Rect (0,0,charlength*10 + 100,20),"Key is " + ky /*+ " value is " + string.Join(",", vl) + " position is " +int.Parse(indx.ToString())*/);
for (int i = 0; i < dictionary[dictionary.Keys.ElementAt(indx)].Length; i++)
{
if (GUI.Button (new Rect(0, 30 + Screen.height-Screen.height + 40* i, 100, 20),dictionary[dictionary.Keys.ElementAt(indx)][i]))
{
for (int i2 = 0; i2 < dictionary[dictionary.Keys.ElementAt(indx)].Length; i2++)
{
string Answer = dictionary[dictionary.Keys.ElementAt(indx)][i2];
if (Answer.EndsWith("(A)"))
{
print ("Correct");
}
else
{
print ("Incorrect");
}
}
}
}
}
}
I assume you're loading the available answers to the form, maybe in labels with buttons beside them or the text of the button. Either way when you load the answers set the tag property of the correct button with an "A". If the tag of the clicked button has an "A" it's correct.
Changing to something this simple that is easily adapted to the structure you already have, would be much better than trying to make something that is inherently flawed work
In the event handler for the button click something like this should work:
void ButtonClck(object sender, eventargs e)
{
Button clickedbutton = (Button)sender;
if ((string)clickedbutton.Tag = "A")
{
print ("Correct");
}
else
{
print ("Incorrect");
}
}
The way to accomplish this is simply use an if statement following the initial buttion click event . you check if the source where that button gets its text is the correct answer .
if (GUI.Button (new Rect(0, 30 + Screen.height-Screen.height + 40* i, 100, 20),dictionary[dictionary.Keys.ElementAt(indx)][i]))
{
if (dictionary[dictionary.Keys.ElementAt(indx)][i].EndsWith ("(A)"))
{
print ("Correct");
}
else
{
print ("Incorrect");
}
}

Creating,using and comparing elements within a button collection

I'm making a Tic-tac-toe project, and I'm having some difficulties. I've only gotten a loose grip about collections, so you can see the problem.
Thing is, I created 9 buttons which change their Background image when clicked, and disable themselves. I've managed to make it run for 2 players, but my wish is to create some sort of an AI.
I need a button collection so I can compare their properties, and avoid the usual one-by-one comparison and massive and numerous If statements.
What I actually want is to be able to test whether buttons in the same row or column have the same values (background images). What I've used so far is an array of strings that describes the sign value, but it's entirely detached from the buttons, and it would take a lot of time to type out all that code.
If it cannot be done the way I imagined it, please do tell. I am open to suggestions, and would be most grateful.
Oh, and if you need any code, or further detail, let me know.
You need to separate data (array) and presentation (buttons).
Update
I published a sample console project that compiles and runs.
Its model is separate from presentation so you can take TicTacToe.cs and write a GUI for it.
You don't need strings; booleans are fine for two states.
In fact, there is a third state, which is empty, so you can use a nullable boolean.
So X would correspond to true, O to false, and empty space to null.
I'd create a class that encapsulates a nullable boolean square array:
class TicTacToe {
const int Length = 3;
private bool? [][] _data;
private bool? _winner;
public TicTacToe ()
{
_data = Enumerable
.Range (0, Length)
.Select (_ => new bool? [Length])
.ToArray ();
}
}
Then I'd represent rows, columns and diagonals as vectors:
public bool? GetCell (int row, int column)
{
return _data [row][column];
}
public IEnumerable<bool?> GetRow (int index)
{
return _data [index];
}
IEnumerable<int> GetIndices ()
{
return Enumerable.Range (0, Length);
}
public IEnumerable<bool?> GetColumn (int index)
{
return GetIndices ()
.Select (GetRow)
.Select (row => row.ElementAt (index));
}
public IEnumerable<bool?> GetDiagonal (bool ltr)
{
return GetIndices ()
.Select (i => Tuple.Create (i, ltr ? i : Length - 1 - i))
.Select (pos => GetCell (pos.Item1, pos.Item2));
}
public IEnumerable<IEnumerable<bool?>> GetRows ()
{
return GetIndices ()
.Select (GetRow);
}
public IEnumerable<IEnumerable<bool?>> GetColumns ()
{
return GetIndices ()
.Select (GetColumn);
}
public IEnumerable<IEnumerable<bool?>> GetDiagonals ()
{
return new [] { true, false }
.Select (GetDiagonal);
}
public IEnumerable<IEnumerable<bool?>> GetVectors ()
{
return GetDiagonals ()
.Concat (GetRows ())
.Concat (GetColumns ());
}
Then I'd write a function that takes a vector and says if it's a winning one:
static bool? FindWinner (IEnumerable<bool?> vector)
{
try {
return vector
.Distinct ()
.Single ();
} catch (InvalidOperationException) {
return null;
}
}
static bool? FindWinner (IEnumerable<IEnumerable<bool?>> vectors)
{
return vectors
.Select (FindWinner)
.FirstOrDefault (winner => winner.HasValue);
}
public bool? FindWinner ()
{
return FindWinner (GetVectors ());
}
Now we can call GetWinner to find out if somebody already won.
Then I'd write a method to make a move:
public bool MakeMove (int row, int column, bool move)
{
if (_winner.HasValue)
throw new InvalidOperationException ("The game is already won.");
if (_data [row][column].HasValue)
throw new InvalidOperationException ("This cell is already taken.");
_data [row][column] = move;
_winner = FindWinner ();
return move == _winner;
}
public bool? Winner {
get { return _winner; }
}
This was all inside TicTacToe class.
Your GUI should create it and call its methods.
When a button gets clicked, this is what you may do:
private TicTacToe _game = new TicTacToe ();
private Button [][] _buttons = new Button [][3];
const bool HumanPlayer = true;
const bool AIPlayer = false;
public void HandleButtonClick (object sender, EventArgs e)
{
// Assuming you put a Tuple with row and column in button's Tag property
var position = (Tuple<int, int>) ((Button) sender).Tag;
var row = position.Item1;
var column = position.Item2;
// Sanity check
Debug.Asset (sender == _buttons [row][column]);
bool won = _game.MakeMove (row, column, HumanPlayer);
if (won) {
MessageBox.Show ("You won.");
}
RefreshButtons ();
}
void RefreshButtons ()
{
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
var btn = _buttons [i][j];
var cell = _game.GetCell (i, j);
btn.Enabled = !cell.HasValue;
btn.Text = cell.HasValue
? (cell.Value ? "X" : "O")
: string.Empty;
}
}
}
Your AI should also call MakeMove and do its calculations based on information from calling GetRow, GetColumn and GetDiagonal.
I didn't check the code, it's only a sketch. (But the console project should run just fine.)
What I would use is a game state class that contains the current state of the game. For example:
public class Game
{
// These will have null for unselected, true for circle, false for cross, or something like that
public bool?[][] SquareStates = new bool?[3][3];
// Maybe a property to show a game is in progress
public bool GameInProgress = false;
// Maybe a function to restart game
public void Restart() { ... }
// And maybe a function to check for a winner
public string CheckWinner() { ... }
// Maybe another function to make AI make its next move
// and updates SquareStates.
public void AINextMove(out int row, out int column) { ... }
}
Once you have a class like that, your form would simply contain an intance of Game and then update the array of SquareStates as buttons are pressed and then checks for winner, calls AINextMove, checks for winner again, then updates its own button states with the new SquareStates.

LINQ List<> Moving Elements up and down

Would it be possible to tell me the best way of moving elements in a List<> up and down.
For example I have a class called Building and Building has a list of Rooms objects List<Room>. The rooms are added to the building by name, but I am using this structure to generate a tree view. The user has the option to move a room up and down within a building.
I was trying to use .Reverse(index, count) but this didn't seem to do anything:
// can this item actually be moved up (is it at the first position in it's current parent?)
if (moveDirection == MoveDirection.UP)
{
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
else
{
// move this room up.
parentBuilding.Rooms.Reverse(roomIndex, 1);
}
}
Create a list extension. Call as List<T>.Move(1, MoveDirection.Up).
public static class ListExtensions
{
public static void Move<T>(this IList<T> list, int iIndexToMove,
MoveDirection direction)
{
if (direction == MoveDirection.Up)
{
var old = list[iIndexToMove - 1];
list[iIndexToMove - 1] = list[iIndexToMove];
list[iIndexToMove] = old;
}
else
{
var old = list[iIndexToMove + 1];
list[iIndexToMove + 1] = list[iIndexToMove];
list[iIndexToMove] = old;
}
}
}
public enum MoveDirection
{
Up,
Down
}
Things to consider
Exception handling - what if you are trying to move the bottom
element down or top element up? You will get an index out of range
because you can't move these up or down.
You could improve this and prevent handling exception by extending
functionality to moving the top element to the bottom element and
bottom element down to the top element etc.
Just do a swap:
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
else
{
// move this room up.
var temp = parentBuilding.Rooms[index-1];
parentBuilding.Rooms[index-1] = parentBuilding.Rooms[index];
parentBuilding.Rooms[index] = temp;
}
Personally, I'd make extension method:
static void Swap<TSource>(this IList<TSource> source, int fromIndex, int toIndex)
{
if (source == null)
throw new ArgumentNullExcpetion("source");
TSource tmp = source[toIndex];
source[toIndex] = source[fromIndex];
source[fromIndex] = tmp;
}
Usage:
if (moveDirection == MoveDirection.UP)
{
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
else
{
// move this room up.
parentBuilding.Rooms.Swap(roomIndex, roomIndex - 1);
}
}
How about using SortedDictionary<int, Room> instead of a list. You could store an index in as a Key of the Dictionary and just swap the values when needed.
Swapping places with the room that used to be above should do it:
int roomIndex = parentBuilding.Rooms.IndexOf(room);
if (roomIndex == 0)
{
return;
}
var wasAbove = parentBuilding.Rooms[roomIndex - 1];
parentBuilding.Rooms[roomIndex - 1] = room;
parentBuilding.Rooms[roomIndex] = wasAbove;
That said, I 'm not sure that this is the best object model for the situation; it's not clear that the order of rooms in the list plays a role, and it's also not clear how a room can be "moved up" -- what does that mean?
It might be better to have a RoomPlacement class that aggregates a room and enough information to locate it, and work with that instead.
try this:
int newIndex = whateverIndexYouWantItAt;
int oldIndex = parentBuilding.Rooms.IndexOf(room);
var item = parentBuilding.Rooms[oldIndex];
list.RemoveAt(oldIndex);
if (newIndex > oldIndex) newIndex--;
parentBuilding.Rooms.Insert(newIndex, item);

Categories