Posted: 13th Apr 2023 0:40
matching a top score (of 3, fewest attempts) in my current project recently yielded intriguing results where i expected the .InsertSorted(Score) to be the first of any that matched but was not:

+ Code Snippet
Function AddScore()
	ThisScore as ScoreData
		ThisScore.Attempts = Attempts
		ThisScore.Date$ = GetCurrentDate() 
	Scores.InsertSorted(ThisScore)

	While Scores.Length > 2
		Scores.Remove()
	EndWhile
	Save()
EndFunction


running some initial tests with .InsertSorted(10) 10 times revealed the following behavior (@ nth time .InsertSorted):
(ascending evens followed by descending odds, then 0)

then, curious as to which index would be returned by .Find always returned 4.

full test code with .Find returning (roughly) "middle -1" behavior:
+ Code Snippet
// Project: Sorted 
// Created: 2023-04-12

// show all errors
SetErrorMode(2)

// set window properties
SetWindowTitle( "Sorted" )
SetWindowSize( 360,720, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window

// set display properties
SetVirtualResolution( 360,720 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
SetPrintSize(24)
CenterWindow()

Type Score
	Value
	NUM
	SPR
EndType
GLOBAL Mode$, Target, Scores as Score []
Mode$ = ".Sort"

Fill()

do
	Print(Mode$)
    If GetPointerPressed() then Fill()

	For x = 0 to Scores.Length
		Print( STR(Scores[x].Value) + " @ " + STR(Scores[x].NUM) )
	Next x

	Print(CHR(10)+".Find = " + STR(Target))
	For x = 0 to Scores.Length
		Print( Scores.Find(Target) )
	next x
	
    Sync()
loop

Function Fill()
	Empty()
	ThisScore as Score
	If Mode$ = ".InsertSorted"
		Target = 10
		For x = 0 to 9
			ThisScore.Value = 10
			ThisScore.Num = x
			Scores.InsertSorted(ThisScore)
		Next x
	Else
		Target=2
		For x = 0 to 9
			ThisScore.Value = Random(1,2)
			ThisScore.Num = x
			Scores.Insert(ThisScore)
		Next x	
		Scores.Sort()
	EndIf
	For x = 0 to Scores.Length
		ThisSPR = CreateSprite(0)
		SetSpriteSize(ThisSPR,10,1)
		Scores[x].SPR = ThisSPR
		SetSpritePosition(ThisSPR,100+(x*10), 100-(Scores[x].NUM))
	Next x
EndFunction

Function Empty()
	If Mode$ = ".InsertSorted" then Mode$ = ".Sort" else Mode$ = ".InsertSorted"
	For x = Scores.Length to 0 Step -1
		DeleteSprite(Scores[x].SPR)
		Scores.Remove(x)
	Next x
EndFunction

Function CenterWindow()
	X = GetMaxDeviceWidth()/2.0 - GetWindowWidth()/2.0
	Y = GetMaxDeviceHeight()/2.0 - GetWindowHeight()/2.0
	SetWindowPosition( X,Y)
EndFunction

note the 2 Modes$ with Mode 2 order returning ascending indices presumably because they were .Insert'd in that order.
ie, reversing the .insert, the 2 sets (1 and 2):
+ Code Snippet
		Target=2
		For x = 9 to 0 Step -1
			ThisScore.Value = Random(1,2)
			ThisScore.Num = x
			Scores.Insert(ThisScore)
		Next x	
		Scores.Sort()


point: this was unexpected for me while the docs never did state that .InsertSorted would set it to the top of same values nor does it state that .Find would find the first instance of the value.

the current functionality is still valuable in .find being lightning fast vs our own custom functions but users need to know what the results actually mean.

meanwhile , my desired (formerly anticipated) behavior will need to be accounted for manually. i can see using .find as a jump start then crawling up from there (which i think i do HERE tho saves are currently broken, it seems), for example.

finally, any related issues such as THIS need to be further explored with this new-found insight while feature requests such as .FindNext() that would rely upon the current .InsertSorted/.find behavior would be affected/should be considered.

IndexOf may also be affected?

please post any additional test code that might reveal additional insight.
Posted: 14th Apr 2023 2:07
Easily fixed by creating a field at the beginning of the type that is a combination of value/date/time
Posted: 14th Apr 2023 2:12
yah, easily accounted for; just posting in case others had the same expectations that i've had for the past couple of years...

that AddScore() function is a new one for me where i thought i'd found a simpler way to maintain the top scores than i had in the past while the .remove() was sometimes removing the last instance of the same score and i thought i was misunderstanding .remove().

in the end, i was right (on that) and wrong (on .InsertSorted())
Posted: 14th Apr 2023 2:14
It would be interesting to see what the underlying C++ functions are
Posted: 14th Apr 2023 2:18
(post above edited around the same time you responded)

i guess it's in here somewhere and i gave up on finding it.
Posted: 14th Apr 2023 3:53
Search for AGKI_ARRAY_FIND_STRING and AGKI_ARRAY_FIND in apps\interpreter\AGKCommonSwitch.h
Posted: 14th Apr 2023 5:36
i dont see how it produces the results i received; now i'm questioning my test code.

then, i wonder if there's some OS-specific issue involved like Round()

doesn't matter, i suppose. if i care enough, i'll code it myself
Posted: 14th Apr 2023 7:36
This is the "find" algorithm: https://github.com/TheGameCreators/AGKTier2/blob/master/apps/interpreter/ProgramData.h#L141

It's a binary search (which would have been a better name for the function):

int high = m_iLength-1;
int low = 0;

starting point is: mid = (high+low)/2;
Your m_iLength is 10, so high = 9, low = 0, It starts checking at index (9-0)/2 = 4 (int division)

For insertsorted, every entry has the same key, so it's inserting in the middle of the list each time
Insert 0 when length = 0, so index 0 => 0
Insert 1 when length = 1, mid = 0 / 2, so index 0 => 1, 0
Insert 2 when length = 2, mid = 1 / 2, so index 0 => 2, 1, 0
Insert 3 when length = 3, mid = 2 / 2, so index 1 => 2, 3, 1, 0
Insert 4 when length = 4, mid = 3 / 2, so index 1 => 2, 4, 3, 1, 0
Insert 5 when length = 5, mid = 4 / 2, so index 2 => 2, 4, 5, 3, 1, 0
etc