The FFNN-based AI is very simple. It's no big magic against it. The AI has the following inputs (mainly raycast distances):

Code:
{$ifdef FewerRays}
      CountSensorRays=9;
      CountSensorOpponentRays=5;
{$else}
      CountSensorWallRays=19;

      CountSensorFloorRays=3;

      CountSensorCeilRays=3;

      CountSensorOpponentRays=36;
{$endif}

     PGameShipSensors=^TGameShipSensors;
     TGameShipSensors=record
      WayPointAngle:single;
      Speed:single;
{$ifdef FewerRays}
      Rays:array[0..CountSensorRays-1] of single;
{$else}
      WallRays:array[0..CountSensorWallRays-1] of single;
      FloorRays:array[0..CountSensorFloorRays-1] of single;
      CeilRays:array[0..CountSensorCeilRays-1] of single;
{$endif}
      OpponentRays:array[0..CountSensorOpponentRays-1] of single;
     end;
and the following output:

Code:
     PGameShipControllerInput=^TGameShipControllerInput;
     TGameShipControllerInput=record
      Steering:single;
      Pitch:single;
      Accelerate:single;
      AirBrakeLeft:single;
      AirBrakeRight:single;
      Nitro:single;
      Fire:single;
     end;
All input & output values are in the 0.0 .. 1.0 range, which'll converted to or from the -1.0 .. 1.0 range, depending of the input/output value kind.

Code:
procedure TGameShip.UpdateAI(DeltaTime:double);
var Counter,Index:longint;
    AnalogControlLowPassFilterCoef:single;
begin
 if TGame(Game).Started then begin

  AnalogControlLowPassFilterCoef:=1.0-exp(-(DeltaTime*100.0));

  FFNN.Inputs[0]:=Sensors.WayPointAngle;
  FFNN.Inputs[1]:=Sensors.Speed;
  Index:=2;
{$ifndef FewerRays}
  for Counter:=0 to CountSensorWallRays-1 do begin
   FFNN.Inputs[Index]:=Sensors.WallRays[Counter];
   inc(Index);
  end;
  for Counter:=0 to CountSensorFloorRays-1 do begin
   FFNN.Inputs[Index]:=Sensors.FloorRays[Counter];
   inc(Index);
  end;
  for Counter:=0 to CountSensorCeilRays-1 do begin
   FFNN.Inputs[Index]:=Sensors.CeilRays[Counter];
   inc(Index);
  end;
{$endif}
  for Counter:=0 to CountSensorOpponentRays-1 do begin
   FFNN.Inputs[Index]:=Sensors.OpponentRays[Counter];
   inc(Index);
  end;

  FFNN.Calculate;

  ControllerInput.Steering:=FloatLerp(ControllerInput.Steering,Min(Max((FFNN.Outputs[0]*2.0)-1.0,-1.0),1.0),AnalogControlLowPassFilterCoef);
  ControllerInput.Accelerate:=FloatLerp(ControllerInput.Accelerate,Min(Max((FFNN.Outputs[1]*2.0)-1.0,-1.0),1.0),AnalogControlLowPassFilterCoef);
  ControllerInput.Pitch:=FloatLerp(ControllerInput.Pitch,Min(Max((FFNN.Outputs[2]*2.0)-1.0,-1.0),1.0),AnalogControlLowPassFilterCoef);
  ControllerInput.AirBrakeLeft:=Min(Max(FFNN.Outputs[3],0.0),1.0);
  ControllerInput.AirBrakeRight:=Min(Max(FFNN.Outputs[4],0.0),1.0);

 end else begin

  FillChar(ControllerInput,SizeOf(TGameShipControllerInput),#0);

 end;
end;
So the FFNN-based AI simulates just human analog-controller input. And the training code is:

Code:
procedure TrainAI;
const TeachIterations=1024;
var i,Inputs,Hidden,Outputs,TeachIteration,Index,SubIndex,TimeStatesIndex,i32,
    Counter:longint;
    TimeStates:array of TGameShipTimeStates;
    TimeState:PGameShipTimeState;
    pn,fn:ansistring;
    fs:TFileStream;
    FFNN:TGameFFNN;
begin
 FFNN:=nil;
 TimeStates:=nil;
 try
{$ifdef FewerRays}
  Inputs:=2+CountSensorRays+CountSensorOpponentRays;
{$else}
  Inputs:=2+CountSensorWallRays+CountSensorFloorRays+CountSensorCeilRays+CountSensorOpponentRays;
{$endif}
  Outputs:=3;
  Hidden:=(Inputs+Outputs)*4;
  FFNN:=TGameFFNN.Create([Inputs,Hidden,Hidden,Outputs],0.3,0.9);
  SetLength(TimeStates,0);
  pn:=IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'aitrainingdata';
  if DirectoryExists(pn) then begin
   pn:=IncludeTrailingPathDelimiter(pn);
   i:=0;
   repeat
    fn:=pn+'dataset'+IntToStr(i)+'.bin';
    if FileExists(fn) then begin
     inc(i);
    end else begin
     break;
    end;                            
   until i>$40000000;
   SetLength(TimeStates,i);
   i:=0;
   repeat
    fn:=pn+'dataset'+IntToStr(i)+'.bin';
    if FileExists(fn) then begin
     if (i+1)>length(TimeStates) then begin
      SetLength(TimeStates,i+1);
     end;
     fs:=TFileStream.Create(fn,fmOpenRead);
     try
      if fs.Read(i32,SizeOf(longint))=SizeOf(longint) then begin
       TimeStates[i].Count:=Max(0,i32);
       SetLength(TimeStates[i].Items,TimeStates[i].Count);
       if TimeStates[i].Count>0 then begin
        for Index:=0 to TimeStates[i].Count-1 do begin
         fs.Read(TimeStates[i].Items[Index].Sensors,SizeOf(TGameShipSensors));
         fs.Read(TimeStates[i].Items[Index].ControllerInput,SizeOf(TGameShipControllerInput));
        end;
       end;
      end;
     finally
      fs.Free;
     end;
     inc(i);
    end else begin
     break;
    end;
   until i>$40000000;
   for TeachIteration:=1 to TeachIterations do begin
    write(#13,'Training AI... ',(TeachIteration/TeachIterations)*100.0:6:2,' %');
    for TimeStatesIndex:=0 to length(TimeStates)-1 do begin
     for Index:=0 to TimeStates[TimeStatesIndex].Count-1 do begin
      TimeState:=@TimeStates[TimeStatesIndex].Items[Index];
      begin
       FFNN.Inputs[0]:=TimeState^.Sensors.WayPointAngle;
       FFNN.Inputs[1]:=TimeState^.Sensors.Speed;
       SubIndex:=2;
{$ifdef FewerRays}
       for Counter:=0 to CountSensorRays-1 do begin
        FFNN.Inputs[SubIndex]:=TimeState^.Sensors.Rays[Counter];
        inc(SubIndex);
       end;
{$else}
       for Counter:=0 to CountSensorWallRays-1 do begin
        FFNN.Inputs[SubIndex]:=TimeState^.Sensors.WallRays[Counter];
        inc(SubIndex);
       end;
       for Counter:=0 to CountSensorFloorRays-1 do begin
        FFNN.Inputs[SubIndex]:=TimeState^.Sensors.FloorRays[Counter];
        inc(SubIndex);
       end;
       for Counter:=0 to CountSensorCeilRays-1 do begin
        FFNN.Inputs[SubIndex]:=TimeState^.Sensors.CeilRays[Counter];
        inc(SubIndex);
       end;
{$endif}
       for Counter:=0 to CountSensorOpponentRays-1 do begin
        FFNN.Inputs[SubIndex]:=TimeState^.Sensors.OpponentRays[Counter];
        inc(SubIndex);
       end;
      end;
      FFNN.Calculate;
      begin
       FFNN.DesiredOutputs[0]:=Min(Max((TimeState^.ControllerInput.Steering+1.0)*0.5,0.0),1.0);
       FFNN.DesiredOutputs[1]:=Min(Max((TimeState^.ControllerInput.Accelerate+1.0)*0.5,0.0),1.0);
       FFNN.DesiredOutputs[2]:=Min(Max((TimeState^.ControllerInput.Pitch+1.0)*0.5,0.0),1.0);
       FFNN.DesiredOutputs[3]:=Min(Max(TimeState^.ControllerInput.AirBrakeLeft,0.0),1.0);
       FFNN.DesiredOutputs[4]:=Min(Max(TimeState^.ControllerInput.AirBrakeRight,0.0),1.0);
      end;
      FFNN.BackPropagate;
     end;
    end;
    write(#13,'Training AI...             ');
    write(#13,'Training AI... ');
    fn:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'assets')+'aibrain.bin';
    fs:=TFileStream.Create(fn,fmCreate);
    try
     FFNN.SaveToStream(fs);
    finally
     fs.Free;
    end;
   end;
  end;
 finally
  FFNN.Free;
  SetLength(TimeStates,0);
 end;
end;
Overall very simple, but it works very well for the small size and simplicity of the code, since the FFNN-based AI is driving almost without any accident and in a very human-style behaviour way (since the recorded training datasets are by-human-drived track rounds).

And Supraleiter supports already most all input possibles, keyboard, gamepad controller, touchscreen (but due to a SDL 2.0 bug, only under Win7, with Win8(.1) has SDL 2.0 some problems yet, due to some small multitouch system API changes from Win7 to Win, accelometer, mouse, touchpad, etc. etc. etc,.