I have Cards:array[1..HalfSize][1..2] of string; where HalfSize is an integer ...
If HalfSize is an integer variable (as opposed to a constant declared earlier) then you clearly want a dynamic array.
For this the syntax is straightforward, and a correctly declared type is readily used as a parameter in routines. For example:
type
TStringArray = array of String;
TCards = array[1..2] of TStringArray;
var
cards: TCards;
procedure SetArraySize(firstDimension: Integer; aCardsArray: TCards);
begin
SetLength(aCardsArray[1], firstDimension);
SetLength(aCardsArray[2], firstDimension);
end;
Subsequently, if you had an array named Cards of type TCards you would call
SetArraySize(HalfSize, Cards);
to adjust your variable-sized Cards array as you want to the dimension of whatever the variable HalfSize contains.
In the days of TurboPascal there was a hack using the
TCards = array[0..0] of String;
syntax coupled with GetMem and FreeMem to contrive a dynamic array. However, none of that convoluted stuff is needed since FPC has had reliable dynamic arrays for many years now.