FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour HIX & AntiGravity apps gallery
Posts: 476
Joined: Sat Feb 03, 2007 06:36 AM
Re: HIX & AntiGravity apps gallery
Posted: Fri Dec 26, 2025 03:43 AM

Que bien! Antonio, esperamos oír y ver todo esto el sábado en el Webinar. Gracias por todo.

Carlos

Posts: 1710
Joined: Tue Oct 28, 2008 06:26 PM
Re: HIX & AntiGravity apps gallery
Posted: Fri Dec 26, 2025 07:16 PM
Antonio Linares wrote:

https://ai.fivetechsoft.com/facturas_mysql.prg

100% coded with HIX + Antigravity

Full mysql support :wink: :!:

Estimado Antonio
Puede publicar los fuentes de éste ejemplo porfa para probar

También el con DBFs

Muchas gracias

Saludos,



Adhemar C.
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: HIX & AntiGravity apps gallery
Posted: Fri Dec 26, 2025 07:20 PM

Estimado Adhemar,

Vamos a publicar todo el código fuente, lo entregaremos mañana durante el webinar.

Para usar la versión de mysql hace falta una actualización de HIX :idea:

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1710
Joined: Tue Oct 28, 2008 06:26 PM
Re: HIX & AntiGravity apps gallery
Posted: Fri Dec 26, 2025 07:59 PM

Perfecto.

Espero para mañana

Gracias.

Saludos,



Adhemar C.
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX & AntiGravity apps gallery
Posted: Thu Jan 01, 2026 06:38 PM

New game hix chess, changes implemented following Antonio's example

Implemented features

  • Valid move check if the king is in check, with the piece flashing
  • Coordinates entry (letter and number notation)
  • Player names "player" and "opponent" and player icons
  • Move archive creation and display of moves next to the board
  • "Load game" and "save game" buttons for future implementations
  • Added a button to invoke a setup procedure with board colors and the option to select a piece type (files are loaded in svg format)
  • New Hix Chess logo
  • Implemented game levels (9): "Beginner", "Intermediate", "Intermediate II", "Advanced", "Expert", "Master", "Grandmaster", "Maximum"
  • Display of a New Game window with player names
  • Implemented play against the computer (there is still a bug)
  • Multilingual support: Italian, English, Spanish, German, can be activated from Settings

there are many files , more function and images are of my Tchess.prg class made on November, I convert them on Hix
I cannot all them insert here.

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX & AntiGravity apps gallery
Posted: Thu Jan 01, 2026 06:55 PM

chess.prg

// Chess Game Logic for HIX
// Uses FEN for state management

function Main()
   local oErr, cHtml
   

   EnsureHistoryDBF()
   

   BEGIN SEQUENCE
      cHtml := ChessGame()
   RECOVER USING oErr
      cHtml := "<h2>Error: " + ValToChar(oErr:Description) + "</h2>"
      cHtml += "<p>Operation: " + ValToChar(oErr:Operation) + "</p>"
      cHtml += "<p>Filename: " + ValToChar(oErr:Filename) + "</p>"
      cHtml += "<p>GenCode: " + ValToChar(oErr:GenCode) + "</p>"
      cHtml += "<p>SubCode: " + ValToChar(oErr:SubCode) + "</p>"
      cHtml += "<p>Args: " + ValToChar(oErr:Args) + "</p>"
   END
   

return cHtml

function ChessGame()
   local hPost := UPost()
   local cHtml := ""
   local cAction := "new", hParams
   local cFEN    := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
   local cGameID := "", cMoveStr, cSelected, cTurn, lSaveMove := .F.
   local aValidMoves := {}, cClicked, cPiece, cTurnColor, lInCheck, lIsMate

   local hConfig
   local cGameMode := "HvH" // HvH or HvPC
   local cPlayer1, cPlayer2, nLevel
   // local cPlayer1  := "Giocatore 1" // Init from Config below
   // local cPlayer2  := "Giocatore 2"
   // local nLevel    := 1 
   local aLevels   := { "Neofita", "Principiante", "Intermedio", "Intermedio II", "Avanzato", "Esperto", "Maestro", "Grande Maestro", "Massimo" }
   

   local cMyColor  := "White"
   local hLang := {=>} // Language hash
   


   EnsureHistoryDBF()
   EnsureSavedGamesDBF()
   hConfig := LoadSettings()
   

   // Load Language
   hLang := LoadLanguage( hConfig["Language"] )
   

   // Init Defaults from Config
   cPlayer1 := iif(hb_HHasKey(hConfig,"Player1"), hConfig["Player1"], T("DEFAULT_PLAYER1", hLang))
   cPlayer2 := iif(hb_HHasKey(hConfig,"Player2"), hConfig["Player2"], T("DEFAULT_PLAYER2", hLang))
   nLevel   := iif(hb_HHasKey(hConfig,"Level"), Val(hConfig["Level"]), 1)
   

   if hb_HHasKey( hPost, "action" )
      cAction := hPost[ "action" ]
   endif
   

   // Read persisted state from Hidden Inputs (if strictly reloading/moving)
   if hb_HHasKey( hPost, "player1" ); cPlayer1 := hPost["player1"]; endif
   if hb_HHasKey( hPost, "player2" ); cPlayer2 := hPost["player2"]; endif
   if hb_HHasKey( hPost, "game_mode" ); cGameMode := hPost["game_mode"]; endif
   if hb_HHasKey( hPost, "level" ); nLevel := Val(hPost["level"]); endif
   if hb_HHasKey( hPost, "my_color" ); cMyColor := hPost["my_color"]; endif
   

   // Handle New Game start
   if cAction == "new_game_start"
       cGameID := GetNewGameID()
       cFEN := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
       

   cPlayer1 := iif( hb_HHasKey(hPost, "player1"), hPost["player1"], "Giocatore 1" )
   cPlayer2 := iif( hb_HHasKey(hPost, "player2"), hPost["player2"], "Giocatore 2" )
   cGameMode := iif( hb_HHasKey(hPost, "game_mode"), hPost["game_mode"], "HvH" )
   if hb_HHasKey( hPost, "level" ); nLevel := Val(hPost["level"]); endif
   cMyColor := iif( hb_HHasKey(hPost, "my_color"), hPost["my_color"], "White" )
   endif
   

   if cAction == "new" .or. (cAction == "new_game_start" .and. Empty(cGameID))
      cGameID := GetNewGameID()
   endif
   if hb_HHasKey( hPost, "fen" )
      cFEN := hPost["fen"]
   else
      cFEN := InitBoard()
   endif

   if hb_HHasKey( hPost, "game_id" )
      cGameID := hPost["game_id"]
   endif

   if Empty( cFEN )
      cFEN := InitBoard()
   endif
   

   if Empty( cGameID )
       cGameID := GetNewGameID()
   endif

   cSelected := ""
   if hb_HHasKey( hPost, "selected" )
      cSelected := hPost["selected"]
   endif

   do case
      case cAction == "new"
         cFEN := InitBoard()
         cGameID := GetNewGameID()
         cSelected := ""
      

  case cAction == "new_game_start"
     cFEN := InitBoard()
     cGameID := GetNewGameID()
     cSelected := ""
     
     // Update vars from Post
     if hb_HHasKey(hPost, "player1"); cPlayer1 := hPost["player1"]; endif
     if hb_HHasKey(hPost, "player2"); cPlayer2 := hPost["player2"]; endif
     if hb_HHasKey(hPost, "level");   nLevel   := Val(hPost["level"]); endif
     
     // Persist to Config
     hConfig["Player1"] := cPlayer1
     hConfig["Player2"] := cPlayer2
     hConfig["Level"]   := AllTrim(Str(nLevel))
     SaveSettings( hConfig )
     
  case cAction == "select"
     

 cClicked := hPost["clicked_square"]
 
 if !Empty( cClicked ) 
     cPiece := GetPieceAt( cFEN, cClicked )
     cTurnColor := GetTurn( cFEN ) 
     
     if cClicked == cSelected
        cSelected := "" // Deselect
     else
        if !Empty(cSelected)
           // Try to move
           if IsValidMove( cFEN, cSelected, cClicked )
              cMoveStr := GetSAN( cFEN, cSelected, cClicked )
              cFEN := MakeMove( cFEN, cSelected, cClicked )
              lSaveMove := .T.
              cSelected := "" // Move done, deselect
           else
              // Invalid move. 
              // If clicked square has own piece, switch selection.
              if IsOwnPiece( cPiece, cTurnColor )
                 cSelected := cClicked
                 aValidMoves := GetLegalMoves( cFEN, cSelected )
              else
                  // Invalid move to empty or enemy square, effectively deselect/reset
                  cSelected := ""
              endif
           endif
        else
           // Nothing selected, selecting now
           if IsOwnPiece( cPiece, cTurnColor )
              cSelected := cClicked
              aValidMoves := GetLegalMoves( cFEN, cSelected )
           endif
        endif
     endif
 endif
 
case cAction == "computer_move"
     // AI Turn
     // Ensure it is computer's turn
     cTurnColor := GetTurn( cFEN )
     if ( cGameMode == "HvPC" ) .and. ( cTurnColor != Left(hPost["my_color"],1) )
         // Calculate Move
         nLevel := Val(hPost["level"])
         cMoveStr := GetComputerMove( cFEN, nLevel, cTurnColor )
         
         if !Empty( cMoveStr )
             // Parse From/To from string (e.g. "e2e4")
             cSelected := Left( cMoveStr, 2 )
             cClicked  := Right( cMoveStr, 2 )
             cFEN := MakeMove( cFEN, cSelected, cClicked )
             lSaveMove := .T.
         endif
     endif
     cSelected := ""
     
     
case cAction == "save_game"
     if hb_HHasKey(hPost, "save_name")
        cGameID := GetNewGameID() // Or use current? Better to generate new ID for saved state or keep current.
        // Actually, if we save, we might want to keep the same ID or not.
        // Let's just save the current snapshot.
        if UseSavedGames()
           Append Blank
           SAVED->GAMEID := iif(Empty(cGameID), GetNewGameID(), cGameID)
           SAVED->NAME   := hPost["save_name"]
           SAVED->FEN    := cFEN
           SAVED->DATE   := Date()
           SAVED->TIME   := Time()
           SAVED->MODE   := cGameMode
           SAVED->P1NAME := cPlayer1
           SAVED->P2NAME := cPlayer2
           Close SAVED
           hParams["status_msg"] := "Partita Salvata: " + hPost["save_name"]
        endif
     endif
     
case cAction == "load_game_list"
     cHtml := ""
     if UseSavedGames()
         Go Bottom
         do while !Bof()
             // list item
             cName := AllTrim(SAVED->NAME)
             cDate := DtoC(SAVED->DATE)
             cID   := AllTrim(SAVED->GAMEID)
             
             cHtml += '<li style="margin-bottom:5px;">' + ;
                      '<button type="button" class="btn" style="width:100%; text-align:left;" ' + ;
                      "onclick='loadGame(" + '"' + cID + '"' + ")'>" + ;
                      '<b>' + cName + '</b> <span style="font-size:12px; float:right;">' + cDate + '</span>' + ;
                      '</button></li>'
             Skip -1
         enddo
         Close SAVED
     endif
     hParams["saved_games_list"] := cHtml
     hParams["open_load_modal"] := "Y"
     
case cAction == "load_game" // triggered by hidden input logic
     // We need a separate JS function `loadGame(id)` to set action='load_game' and game_id input
     if hb_HHasKey(hPost, "game_id")
         if UseSavedGames()
             Locate for AllTrim(GAMEID) == AllTrim(hPost["game_id"])
             if Found()
                 cFEN      := AllTrim(SAVED->FEN)
                 cGameID   := AllTrim(SAVED->GAMEID)
                 cGameMode := AllTrim(SAVED->MODE)
                 cPlayer1  := AllTrim(SAVED->P1NAME)
                 cPlayer2  := AllTrim(SAVED->P2NAME)
                 hParams["status_msg"] := "Partita Caricata: " + AllTrim(SAVED->NAME)
             endif
             Close SAVED
         endif
     endif

case cAction == "save_settings"
    hConfig["SquareWhite"] := hPost["square_white"]
    hConfig["SquareBlack"] := hPost["square_black"]
    hConfig["Background"]  := hPost["background"]
    hConfig["ShowCoords"]  := iif( hb_HHasKey(hPost,"show_coords"), .T., .F. )
    hConfig["CoordsMode"]  := hPost["coords_mode"] // "Normal" or "Full"
    hConfig["Theme"]       := hPost["theme"]
    hConfig["TextColor"]   := hPost["text_color"]
    if hb_HHasKey(hPost, "language")
       hConfig["Language"] := hPost["language"]
       // Reload language for immediate effect
       hLang := LoadLanguage( hConfig["Language"] )
    endif
    SaveSettings( hConfig )
    // Stay on current FEN/Game
    
   endcase
   

   if lSaveMove
       SaveMoveToDBF( cGameID, cMoveStr, cFEN )
   endif

   // Post-move/action status check
   cTurnColor := GetTurn(cFEN)
   lInCheck := IsCheck( cFEN, cTurnColor )
   lIsMate := .F.
   

   if lInCheck
      lIsMate := IsCheckmate( cFEN, cTurnColor )
   endif
   

   

   hParams := {;
      "fen" => cFEN, ;
      "board_html" => RenderBoard( cFEN, cSelected, aValidMoves, lInCheck, hConfig, (cMyColor == "Black") ), ;
      "turn" => iif( cTurnColor == "w", T("WHITE", hLang), T("BLACK", hLang) ), ;
      "status_msg" => iif( lIsMate, T("CHECKMATE", hLang) + " " + iif(cTurnColor=="w",T("BLACK", hLang),T("WHITE", hLang)) + " " + T("WINS", hLang), iif(lInCheck, T("CHECK", hLang), "") ), ;
      "selected" => cSelected, ;
      "game_id" => cGameID, ;
      "moves_list" => GetMovesHTML( cGameID ), ;
      "player1" => cPlayer1, ;
      "player2" => cPlayer2, ;
      "game_mode" => cGameMode, ;
      "level" => AllTrim(Str(nLevel)), ;
      "my_color" => cMyColor, ;
      "computer_thinking" => iif( (cGameMode == "HvPC" .and. cTurnColor != Left(cMyColor,1) .and. !lIsMate), "Y", "N" ), ;
      "cfg_white" => hConfig["SquareWhite"], ;
      "cfg_black" => hConfig["SquareBlack"], ;
      "cfg_bg" => hConfig["Background"], ;
      "cfg_coord_chk" => iif(hConfig["ShowCoords"], "checked", ""), ;
      "chk_normal" => iif(hConfig["CoordsMode"] == "Normal", "checked", ""), ;
      "chk_full" => iif(hConfig["CoordsMode"] == "Full", "checked", ""), ;
      "cfg_theme" => hConfig["Theme"], ;
      "cfg_text" => hConfig["TextColor"], ;
      "theme_options" => GetThemesOptions( hConfig["Theme"] ), ;
      "language_options" => GetLanguageOptions( hConfig["Language"] ), ;
      "Player1" => cPlayer1, ;
      "Player2" => cPlayer2, ;
      "has_active_action" => iif( !Empty(cAction) .and. cAction != "new", "Y", "N" ), ;
      "splash_style" => iif( !Empty(cAction) .and. cAction != "new", "none !important", "flex" ), ;
      "L_NEW_GAME" => T("NEW_GAME", hLang), ;
      "L_LOAD_GAME" => T("LOAD_GAME", hLang), ;
      "L_SAVE_GAME" => T("SAVE_GAME", hLang), ;
      "L_EXIT" => T("EXIT", hLang), ;
      "L_HISTORY" => T("HISTORY", hLang), ;
      "L_PIECES" => T("PIECES", hLang), ;
      "L_BOARD" => T("BOARD", hLang), ;
      "L_BOARD_DEFAULT" => T("BOARD_DEFAULT", hLang), ;
      "L_BOARD_WOOD" => T("BOARD_WOOD", hLang), ;
      "L_SHOW_COORDS" => T("SHOW_COORDS", hLang), ;
      "L_COORDS_OUTSIDE" => T("COORDS_OUTSIDE", hLang), ;
      "L_COORDS_FULL" => T("COORDS_FULL", hLang), ;
      "L_PIECE_NOTATION" => T("PIECE_NOTATION", hLang), ;
      "L_ICONS" => T("ICONS", hLang), ;
      "L_TEXT_COLOR" => T("TEXT_COLOR", hLang), ;
      "L_LANGUAGE" => "Language", ;
      "L_CANCEL" => T("CANCEL", hLang), ;
      "L_SAVE" => T("SAVE", hLang), ;
      "L_NEW_GAME_TITLE" => T("NEW_GAME_TITLE", hLang), ;
      "L_GAME_TYPE" => T("GAME_TYPE", hLang), ;
      "L_HUMAN_VS_HUMAN" => T("HUMAN_VS_HUMAN", hLang), ;
      "L_HUMAN_VS_PC" => T("HUMAN_VS_PC", hLang), ;
      "L_PLAYER1" => T("PLAYER1", hLang), ;
      "L_PLAYER2" => T("PLAYER2", hLang), ;
      "L_AI_LEVEL" => T("AI_LEVEL", hLang), ;
      "L_YOUR_COLOR" => T("YOUR_COLOR", hLang), ;
      "L_START" => T("START", hLang), ;
      "L_LEVEL_1" => T("LEVEL_1", hLang), ;
      "L_LEVEL_2" => T("LEVEL_2", hLang), ;
      "L_LEVEL_3" => T("LEVEL_3", hLang), ;
      "L_LEVEL_4" => T("LEVEL_4", hLang), ;
      "L_LEVEL_5" => T("LEVEL_5", hLang), ;
      "L_LEVEL_6" => T("LEVEL_6", hLang), ;
      "L_LEVEL_7" => T("LEVEL_7", hLang), ;
      "L_LEVEL_8" => T("LEVEL_8", hLang), ;
      "L_LEVEL_9" => T("LEVEL_9", hLang), ;
      "L_SAVE_GAME_TITLE" => T("SAVE_GAME_TITLE", hLang), ;
      "L_GAME_NAME" => T("GAME_NAME", hLang), ;
      "L_ENTER_NAME" => T("ENTER_NAME", hLang), ;
      "L_LOAD_GAME_TITLE" => T("LOAD_GAME_TITLE", hLang), ;
      "L_CLOSE" => T("CLOSE", hLang), ;
      "L_COMPUTER_THINKING" => T("COMPUTER_THINKING", hLang), ;
      "L_ENTER_NAME_ALERT" => T("ENTER_NAME_ALERT", hLang) ;
   }
   

   cHtml := View( "chess", hParams )
   

   // Removed <h2> injection. Status now handled by Modal via hParams["status_msg"]
   

return cHtml

//----------------------------------------------------------------------------//

function InitBoard()
   // Standard starting position FEN
   return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"

function GetTurn( cFEN )
   local aParts := hb_ATokens( cFEN, " " )
   if Len(aParts) >= 2
      return aParts[2]
   endif
return "w"

function GetPieceAt( cFEN, cSquare )
   local aGrid := FENToGrid( cFEN )
   local aCoords := SquareToCoords( cSquare )
   return aGrid[ aCoords[1], aCoords[2] ]

function IsOwnPiece( cPiece, cTurnColor )
   if Empty(cPiece); return .F.; endif
   if cTurnColor == "w"
      return IsUpperLocal( cPiece ) // White pieces are uppercase
   else
      return !IsUpperLocal( cPiece ) // Black pieces are lowercase
   endif
return .F.

function GetSAN( cFEN, cFrom, cTo )
   local cSAN := ""
   local cPiece := GetPieceAt( cFEN, cFrom )
   local cTarget := GetPieceAt( cFEN, cTo )
   local lCapture := !Empty( cTarget )
   local cType := Upper(cPiece)
   local cTargetCol := Left(cTo, 1) // e.g., 'e' from 'e4'
   local cSourceCol := Left(cFrom, 1) // e.g., 'd' from 'd4'
   local cNextFEN, lCheck, lMate, cTurn, cOppColor
   

   // It's technically possible for En Passant to be a capture where cTarget is empty but the move is diagonal pawn.
   // Simplified: IsOwnPieces check handled by valid move logic.
   // But to correctly mark 'x', check if diagonal pawn move to empty square (En Passant) or standard capture.
   if cType == "P" .and. cSourceCol != cTargetCol .and. Empty(cTarget)
      lCapture := .T. // En Passant
   endif

   // Piece Notation (Italian)
   do case
      case cType == "P"
         cSAN := ""
      case cType == "K"; cSAN := "R"
      case cType == "Q"; cSAN := "D"
      case cType == "R"; cSAN := "T"
      case cType == "B"; cSAN := "A"
      case cType == "N"; cSAN := "C"
   endcase
   

   // Captures
   if lCapture
      if cType == "P"
         cSAN += cSourceCol
      endif
      cSAN += "x"
   endif
   

   cSAN += cTo
   

   // Check/Mate detection (Simulate move)
   cNextFEN := MakeMove( cFEN, cFrom, cTo, .T. )
   cTurn := GetTurn( cFEN ) // Who moved?
   // MakeMove toggles turn, so cNextFEN has opponent to move.
   // We want to see if Opponent is in check.
   // Opponent Color:
   cOppColor := iif( cTurn == "w", "b", "w" )
   

   lCheck := IsCheck( cNextFEN, cOppColor )
   if lCheck
      if IsCheckmate( cNextFEN, cOppColor )
         cSAN += "#"
      else
         cSAN += "+"
      endif
   endif
   

   // TODO: Castling (0-0, 0-0-0) logic would go here updating cSAN
   // Detecting castling from from/to squares:
   if cType == "K" .and. Abs( Asc(Left(cFrom,1)) - Asc(Left(cTo,1)) ) == 2
       if cTo > cFrom // Kingside? (e.g. e1 -> g1)
           cSAN := "0-0"
       else
           cSAN := "0-0-0"
       endif
   endif
   

return cSAN
//----------------------------------------------------------------------------//
// Persistence

//----------------------------------------------------------------------------//
// Persistence

function EnsureHistoryDBF()
   local aStruct := {;
      { "GAMEID", "C", 14, 0 }, ;
      { "SEQ",    "N",  3, 0 }, ;
      { "MOVE",   "C", 10, 0 }, ;
      { "FEN",    "C", 90, 0 }, ;
      { "TS",     "C", 14, 0 }  ;
   }
   local cPath := "c:\hix\mosse\"
   

   if !IsDirectory( cPath )
      DirMake( cPath )
   endif
   

   if !File( cPath + "moves.dbf" )
      dbCreate( cPath + "moves.dbf", aStruct )
   endif

   if !File( cPath + "moves_gid.cdx" )
      use ( cPath + "moves" ) exclusive new
      index on GAMEID tag GAMEID
      use
   endif
return nil

function EnsureSavedGamesDBF()
   local cPath := "c:\hix\mosse\"
   

   if !IsDirectory( cPath )
      DirMake( cPath )
   endif
   

   if !File( cPath + "saved.dbf" )
      DbCreate( cPath + "saved", {;
         { "GAMEID",   "C", 20, 0 },; 
         { "NAME",     "C", 40, 0 },; 
         { "FEN",      "C", 200, 0 },;
         { "DATE",     "D",  8, 0 },;
         { "TIME",     "C",  8, 0 },;
         { "MODE",     "C", 10, 0 },; // HvH or HvPC
         { "P1NAME",   "C", 30, 0 },;
         { "P2NAME",   "C", 30, 0 } ;
      })
   endif
   // No complex index needed for now, maybe by Date
return nil

function GetNewGameID()
   return DtoS(Date()) + StrTran(Time(), ":", "")

function SaveMoveToDBF( cGameID, cMove, cFEN )
   local nSeq := 1
   

   USE "c:\hix\mosse\moves.dbf" SHARED NEW ALIAS "MOVES"
   SET INDEX TO "c:\hix\mosse\moves_gid"
   

   COUNT TO nSeq FOR FIELD->GAMEID == cGameID
   nSeq++
   

   APPEND BLANK
   FIELD->GAMEID := cGameID
   FIELD->SEQ    := nSeq
   FIELD->MOVE   := cMove
   FIELD->FEN    := cFEN
   FIELD->TS     := DtoS(Date()) + StrTran(Time(), ":", "")
   

   USE
return nil

function GetMovesHTML( cGameID )
   local cHtml := "<ul>"
   local cClass
   

   USE "c:\hix\mosse\moves.dbf" SHARED NEW ALIAS "MOVES"
   

   SET FILTER TO FIELD->GAMEID == cGameID
   GO TOP
   

   while !Eof()
       cHtml += "<li>" + AllTrim(Str(FIELD->SEQ)) + ". " + FIELD->MOVE + "</li>"
       SKIP
   end
   

   USE
   cHtml += "</ul>"
return cHtml

//----------------------------------------------------------------------------//

function IsCheckmate( cFEN, cColor )
   local aGrid := FENToGrid( cFEN )
   local r, c, cPiece
   local aMoves
   

   // Check every piece of current color
   for r := 1 to 8
      for c := 1 to 8
         cPiece := aGrid[r,c]
         if !Empty(cPiece)
            if (cColor == "w" .and. IsUpperLocal(cPiece)) .or. (cColor == "b" .and. !IsUpperLocal(cPiece))
               // It's my piece. Can I move it legally?
               aMoves := GetLegalMoves( cFEN, CoordsToSquare(r,c) )
               if Len(aMoves) > 0
                  return .F. // Found at least one legal move
               endif
            endif
         endif
      next
   next
   

return .T. // No legal moves found

function IsValidMove( cFEN, cFrom, cTo )
   local aMoves := GetLegalMoves( cFEN, cFrom )
   local i
   for i := 1 to Len(aMoves)
      if aMoves[i] == cTo
         return .T.
      endif
   next
return .F.

function GetLegalMoves( cFEN, cFrom )
   local aPseudoMoves := GetPseudoValidMoves( cFEN, cFrom )
   local aLegalMoves := {}
   local i, cNextFEN, cTurn
   

   cTurn := GetTurn( cFEN )
   

   for i := 1 to Len(aPseudoMoves)
      // Simulate move
      cNextFEN := MakeMove( cFEN, cFrom, aPseudoMoves[i], .T. ) // .T. = Simulation mode (same turn color for check test)
      // Check if own king is in check in the new state
      // Note: MakeMove normally toggles turn. For checking legality, we need to check if the 
      // player who MOVED is now in check. MakeMove returns FEN with NEXT turn.
      // So if White moved, cNextFEN has Black to move.
      // We need to check if White King is attacked by Black pieces in cNextFEN.
      

  if !IsCheck( cNextFEN, cTurn )
     AAdd( aLegalMoves, aPseudoMoves[i] )
  endif
   next
return aLegalMoves

function IsCheck( cFEN, cColor ) // cColor is the King's color we are checking
   local aGrid := FENToGrid( cFEN )
   local r, c, cPiece
   local nKingRow, nKingCol
   local lFound := .F.
   local cHtml := ""
   

   // Find King
   for r := 1 to 8
      for c := 1 to 8
         cPiece := aGrid[r,c]
         if !Empty(cPiece) .and. Upper(cPiece) == "K" .and. ;
            ( (cColor == "w" .and. IsUpperLocal(cPiece)) .or. (cColor == "b" .and. !IsUpperLocal(cPiece)) )
            nKingRow := r
            nKingCol := c
            lFound := .T.
            exit
         endif
      next
      if lFound; exit; endif
   next
   

   if !lFound; return .F.; endif // Should not happen
   

   // Check if King square is attacked by Enemy
   return IsSquareAttacked( aGrid, nKingRow, nKingCol, cColor )

function IsSquareAttacked( aGrid, nRow, nCol, cDefendingColor )
   local r, c, i, cPiece, cType, cEnemyColor
   local aDirs, aOffs
   

   // Check attacks from all directions
   

   // 1. Pawn Attacks
   // If I am White, check if Black pawns are at (Row-1, Col+/-1) attacking me
   // Actually, White King at Row X. Black Pawn at Row X-1 (visually "above" if White is bottom-up, but Grid Row 1 is Top).
   // Grid: Row 1=Rank 8 (Black side), Row 8=Rank 1 (White side).
   // White Pawn moves r->r-1. Captures at r-1.
   // Black Pawn moves r->r+1. Captures at r+1.
   

   if cDefendingColor == "w"
      // Check for Black Pawns at r-1, c+/-1
      r := nRow - 1
      if r >= 1
         if nCol > 1 .and. aGrid[r, nCol-1] == "p"; return .T.; endif
         if nCol < 8 .and. aGrid[r, nCol+1] == "p"; return .T.; endif
      endif
   else
      // Check for White Pawns at r+1, c+/-1
      r := nRow + 1
      if r <= 8
         if nCol > 1 .and. aGrid[r, nCol-1] == "P"; return .T.; endif
         if nCol < 8 .and. aGrid[r, nCol+1] == "P"; return .T.; endif
      endif
   endif
   

   // 2. Knights
   aOffs := { {-2,-1}, {-2,1}, {-1,-2}, {-1,2}, {1,-2}, {1,2}, {2,-1}, {2,1} }
   for i := 1 to 8
      r := nRow + aOffs[i][1]
      c := nCol + aOffs[i][2]
      if IsOnBoard(r, c)
         cPiece := aGrid[r,c]
         if !Empty(cPiece)
             // Enemy Knight?
             if Upper(cPiece) == "N" .and. !IsOwnPiece(cPiece, cDefendingColor)
                return .T.
             endif
         endif
      endif
   next
   

   // 3. Sliding Pieces (Rook, Bishop, Queen)
   // Straight (Rook/Queen)
   aDirs := { {-1,0}, {1,0}, {0,-1}, {0,1} }
   for i := 1 to 4
      r := nRow + aDirs[i][1]
      c := nCol + aDirs[i][2]
      while IsOnBoard(r, c)
         cPiece := aGrid[r,c]
         if !Empty(cPiece)
            if !IsOwnPiece(cPiece, cDefendingColor)
               cType := Upper(cPiece)
               if cType == "R" .or. cType == "Q"
                  return .T.
               endif
            endif
            exit // Blocked
         endif
         r += aDirs[i][1]
         c += aDirs[i][2]
      end
   next
   

   // Diagonal (Bishop/Queen)
   aDirs := { {-1,-1}, {-1,1}, {1,-1}, {1,1} }
   for i := 1 to 4
      r := nRow + aDirs[i][1]
      c := nCol + aDirs[i][2]
      while IsOnBoard(r, c)
         cPiece := aGrid[r,c]
         if !Empty(cPiece)
            if !IsOwnPiece(cPiece, cDefendingColor)
               cType := Upper(cPiece)
               if cType == "B" .or. cType == "Q"
                  return .T.
               endif
            endif
            exit // Blocked
         endif
         r += aDirs[i][1]
         c += aDirs[i][2]
      end
   next
   

   // 4. Enemy King (King cannot be next to King)
   for r := nRow - 1 to nRow + 1
      for c := nCol - 1 to nCol + 1
         if IsOnBoard(r,c) .and. (r != nRow .or. c != nCol)
            cPiece := aGrid[r,c]
            if !Empty(cPiece) .and. Upper(cPiece) == "K" .and. !IsOwnPiece(cPiece, cDefendingColor)
               return .T.
            endif
         endif
      next
   next

return .F.

function MakeMove( cFEN, cFrom, cTo, lSimulate )
   local aGrid := FENToGrid( cFEN )
   local aFrom := SquareToCoords( cFrom )
   local aTo := SquareToCoords( cTo )
   local cPiece := aGrid[ aFrom[1], aFrom[2] ]
   local cTurn := GetTurn( cFEN )
   local cNextTurn := iif( cTurn == "w", "b", "w" )
   

   // Move piece
   aGrid[ aTo[1], aTo[2] ] := cPiece
   aGrid[ aFrom[1], aFrom[2] ] := ""
   

   // TODO: Handle Pawn Promotion, Castling, En Passant logic updates
   // For now, simple movement
   

   return GridToFEN( aGrid ) + " " + cNextTurn + " KQkq - 0 1" // Simply toggle turn, preserve rights for now

//----------------------------------------------------------------------------//

function GetPseudoValidMoves( cFEN, cFrom )
   local aMoves := {}
   local aGrid := FENToGrid( cFEN )
   local aFrom := SquareToCoords( cFrom )
   local nRow := aFrom[1]
   local nCol := aFrom[2]
   local cPiece := aGrid[nRow, nCol]
   local cType := Upper( cPiece )
   local cColor := iif( IsUpperLocal(cPiece), "w", "b" )
   local i, j, r, c
   local nDir, nStartRow, nTemp
   local aCapCols, aOffs, aDirs
   

   if Empty(cPiece); return {}; endif
   

   // Simplified Move Logic (No check validation yet)
   do case
      case cType == "P" // Pawn
         nDir := iif( cColor == "w", -1, 1 ) // White moves up (row decrements)
         nStartRow := iif( cColor == "w", 7, 2 )
         

 // Move forward 1
 r := nRow + nDir
 if IsOnBoard(r, nCol) .and. Empty( aGrid[r, nCol] )
     AAdd( aMoves, CoordsToSquare(r, nCol) )
     // Move forward 2
     if nRow == nStartRow
         r := nRow + (nDir * 2)
         if IsOnBoard(r, nCol) .and. Empty( aGrid[r, nCol] )
             AAdd( aMoves, CoordsToSquare(r, nCol) )
         endif
     endif
 endif
 
 // Captures
 aCapCols := { nCol - 1, nCol + 1 }
 for i := 1 to 2
    c := aCapCols[i]
    r := nRow + nDir
    if IsOnBoard(r, c)
       if !Empty( aGrid[r,c] ) .and. !IsOwnPiece( aGrid[r,c], cColor )
          AAdd( aMoves, CoordsToSquare(r, c) )
       endif
    endif
 next
 
  case cType == "N" // Knight
     aOffs := { {-2,-1}, {-2,1}, {-1,-2}, {-1,2}, {1,-2}, {1,2}, {2,-1}, {2,1} }
     for i := 1 to Len(aOffs)
        r := nRow + aOffs[i][1]
        c := nCol + aOffs[i][2]
        if IsOnBoard(r, c)
           if Empty( aGrid[r,c] ) .or. !IsOwnPiece( aGrid[r,c], cColor )
              AAdd( aMoves, CoordsToSquare(r, c) )
           endif
        endif
     next
     

  case cType == "R" .or. cType == "B" .or. cType == "Q" // Sliders
     aDirs := {}
     if cType == "R" .or. cType == "Q" // Rook dirs
         AAdd( aDirs, {-1, 0} ); AAdd( aDirs, {1, 0} ); AAdd( aDirs, {0, -1} ); AAdd( aDirs, {0, 1} )
     endif
     if cType == "B" .or. cType == "Q" // Bishop dirs
         AAdd( aDirs, {-1, -1} ); AAdd( aDirs, {-1, 1} ); AAdd( aDirs, {1, -1} ); AAdd( aDirs, {1, 1} )
     endif
     

 for i := 1 to Len(aDirs)
     r := nRow + aDirs[i][1]
     c := nCol + aDirs[i][2]
     while IsOnBoard(r, c)
         if Empty( aGrid[r,c] )
             AAdd( aMoves, CoordsToSquare(r, c) )
         else
             if !IsOwnPiece( aGrid[r,c], cColor )
                 AAdd( aMoves, CoordsToSquare(r, c) )
             endif
             exit // Blocked
         endif
         r += aDirs[i][1]
         c += aDirs[i][2]
     end
 next
 
  case cType == "K" // King
     for r := nRow - 1 to nRow + 1
        for c := nCol - 1 to nCol + 1
           if IsOnBoard(r, c) .and. (r != nRow .or. c != nCol)
              if Empty( aGrid[r,c] ) .or. !IsOwnPiece( aGrid[r,c], cColor )
                 AAdd( aMoves, CoordsToSquare(r, c) )
              endif
           endif
        next
     next
     

   endcase
   

return aMoves

function IsOnBoard( r, c )
   return r >= 1 .and. r <= 8 .and. c >= 1 .and. c <= 8

//----------------------------------------------------------------------------//
// FEN/Grid Helpers

function FENToGrid( cFEN )
   local aGrid[8,8]
   local aParts, cBoard, aRows, cRow
   local i, j, r, c, k, ch, nCount
   

   aParts := hb_ATokens( cFEN, " " )
   cBoard := aParts[1]
   aRows := hb_ATokens( cBoard, "/" )
   

   for r := 1 to 8
      cRow := aRows[r]
      c := 1
      for k := 1 to Len(cRow)
         ch := SubStr( cRow, k, 1 )
         if IsDigit( ch )
            nCount := Val( ch )
            for j := 1 to nCount
               aGrid[r, c] := ""
               c++
            next
         else
            aGrid[r, c] := ch
            c++
         endif
      next
   next
return aGrid

function GridToFEN( aGrid )
   local cFEN := ""
   local r, c, i, nEmpty
   

   for r := 1 to 8
      nEmpty := 0
      for c := 1 to 8
         if Empty( aGrid[r,c] )
            nEmpty++
         else
            if nEmpty > 0
               cFEN += AllTrim(Str(nEmpty))
               nEmpty := 0
            endif
            cFEN += aGrid[r,c]
         endif
      next
      if nEmpty > 0
         cFEN += AllTrim(Str(nEmpty))
      endif
      if r < 8; cFEN += "/"; endif
   next
return cFEN

function SquareToCoords( cSquare )
   local cCol := Left( cSquare, 1 )
   local cRow := Right( cSquare, 1 )
   local nCol := Asc( cCol ) - Asc("a") + 1
   local nRow := 8 - (Val( cRow ) - 1) 
   // Matrix: Row 1 is Rank 8 (Top), Row 8 is Rank 1 (Bottom)
   return { nRow, nCol }

function CoordsToSquare( r, c )
   local cCol := Chr( Asc("a") + c - 1 )
   local cRow := AllTrim(Str( 8 - (r - 1) ))
   return cCol + cRow

//----------------------------------------------------------------------------//
// Rendering


function RenderBoard( cFEN, cSelected, aValidMoves, lInCheck, hConfig, lFlip )
   local cHtml := ""
   local nRank, nFile
   local lIsWhite := iif( lFlip == NIL, .T., !lFlip ) // Default perspective White unless lFlip
   local cPiece, cColorClass, cContent
   local aGrid := FENToGrid( cFEN )
   

   local lShowCoords := iif( HB_HHasKey(hConfig, "ShowCoords"), hConfig["ShowCoords"], .F. )
   local cCoordsMode := iif( HB_HHasKey(hConfig, "CoordsMode"), hConfig["CoordsMode"], "Normal" )
   

   // Logic for Coords Visibility
   local lShowCoordsLeft := lShowCoords
   local lShowCoordsRight := lShowCoords .and. ( cCoordsMode == "Full" )
   local lShowCoordsTop := lShowCoords .and. ( cCoordsMode == "Full" )
   local lShowCoordsBottom := lShowCoords
   local cTextColor := hConfig["TextColor"]
   

   cHtml += '<style>.blink-red { animation: blinker 1s linear infinite; background-color: red !important; } @keyframes blinker { 50% { opacity: 0.5; } }'
   

   if HB_HHasKey( hConfig, "SquareWhite" )
      cHtml += ' .white-square { background-color: ' + hConfig["SquareWhite"] + ' !important; }'
      cHtml += ' .black-square { background-color: ' + hConfig["SquareBlack"] + ' !important; }'
   endif
   

   cHtml += ' .coord-cell { color: ' + cTextColor + ' !important; }'
   cHtml += ' .player-info { color: ' + cTextColor + ' !important; }'
   cHtml += ' .square svg { width: 100%; height: 100%; }'
   

   cHtml += '</style>'

   // Wrapper for Board Only (Grid)
   cHtml += '<div class="chess-board-wrapper' + iif( hConfig["Background"]=="wood", " wood-bg", "" ) + '">'
   

   // We will return HTML with:
   // <div class="player-top">...</div>
   // <div class="board-wrapper">...</div>
   // <div class="player-bottom">...</div>
   // But wait, RenderBoard returns just the HTML string to be injected.
   // The View has structure:
   // <form> ... {{board_html}} ... </form>
   // So returning 3 divs is fine, but we need to ensure styling handles them.
   // Wait, current CSS expects wrapper to be the grid.
   // Better: 
   // <div style="display:flex; flex-direction:column; align-items:center;">
   //    <div class="player-info">Player 2</div>
   //    <div class="board-frame-grid"> Use Grid here for Coords+Board </div>
   //    <div class="player-info">Player 1</div>
   // </div>
   

   // Reset cHtml to build this structure
   cHtml := ""
   cHtml += '<style>.blink-red { animation: blinker 1s linear infinite; background-color: red !important; } @keyframes blinker { 50% { opacity: 0.5; } }'
   if HB_HHasKey( hConfig, "SquareWhite" )
      cHtml += ' .white-square { background-color: ' + hConfig["SquareWhite"] + ' !important; }'
      cHtml += ' .black-square { background-color: ' + hConfig["SquareBlack"] + ' !important; }'
   endif
   cHtml += ' .coord-cell { color: ' + cTextColor + ' !important; font-weight:bold; }' // Bold coords
   cHtml += ' .player-info { color: ' + cTextColor + ' !important; width: 100%; display: flex; align-items: center; gap: 10px; margin: 5px 0; font-weight: bold; }'
   cHtml += ' .player-info img { width: 30px; height: 30px; border-radius: 4px; background-color: rgba(255,255,255,0.2); }'
   cHtml += ' .square svg { width: 100%; height: 100%; }'
   cHtml += '</style>'

   // Container
   cHtml += '<div style="display:flex; flex-direction:column; align-items:flex-start; width: fit-content; margin: 0 auto;">'

   // PLAYER 2 (Top - Black usually, or Opponent)
   cHtml += '<div class="player-info">'
   cHtml += '<img src="players/black_400.png" alt="Black"><span>' + iif(hb_HHasKey(hConfig,"Player2"),hConfig["Player2"],"Giocatore 2") + '</span>'
   cHtml += '</div>'
   

   // BOARD FRAME (The "Wood" part with coordinates)
   cHtml += '<div class="chess-board-wrapper' + iif( hConfig["Background"]=="wood", " wood-bg", "" ) + '">'
   

   // Flex Container for Center (Left Coords | Board | Right Gutter)
   cHtml += '<div style="display:flex; flex-direction:row; align-items: flex-start;">'
   

   // 1. LEFT COORDS (Numbers) - Width 20px
   cHtml += '<div style="display:grid; grid-template-rows: repeat(8, 50px); width: 20px; align-items: center; justify-items: center; margin-right: 5px;">'
   if lShowCoords
       for nRank := 1 to 8
           cContent := iif( lIsWhite, AllTrim(Str(9-nRank)), AllTrim(Str(nRank)) )
           cHtml += '<div class="coord-cell">' + cContent + '</div>'
       next
   endif
   cHtml += '</div>'
   

   // 2. BOARD (The Squares)
   cHtml += '<div class="board-container" style="border: 5px solid #4a3021;">'


   

   // Board
   cHtml += '<div class="chess-board">'
   

   if lInCheck == nil; lInCheck := .F.; endif
   cTurn := GetTurn( cFEN )
   

   for nRank := 1 to 8
      for nFile := 1 to 8
         // Calculate Grid Coordinates
         r := iif( lIsWhite, nRank, 9 - nRank )
         c := iif( lIsWhite, nFile, 9 - nFile )
         

     cSquare := CoordsToSquare( r, c )
     cPiece := aGrid[r, c]
     
     // Background color (Visual checkerboard depends on nRank+nFile)
     if (nRank + nFile) % 2 == 0
        cClass := "white-square"
     else
        cClass := "black-square"
     endif
     
     // Selection highlight
     if cSquare == cSelected
        cClass += " selected"
     endif
     
     // King Check Highlight
     if lInCheck .and. !Empty(cPiece) .and. Upper(cPiece) == "K"
         // Check if it's the current turn's king
         if (cTurn == "w" .and. IsUpperLocal(cPiece)) .or. (cTurn == "b" .and. !IsUpperLocal(cPiece))
             cClass += " blink-red"
         endif
     endif
 
 // Piece
 if !Empty(cPiece)
     cContent := GetPieceSymbol( cPiece, hConfig["Theme"] )
     cColorClass := iif( IsUpperLocal(cPiece), " piece-white", " piece-black" )
 else
     cContent := ""
     cColorClass := ""
 endif

 // Valid move marker
 lIsValid := .F.
 if ASCAN( aValidMoves, cSquare ) > 0
    cClass += " valid-move"
    lIsValid := .T.
 endif
 
 cHtml += '<div class="square ' + cClass + '" '
 cHtml += 'onclick="selectSquare(' + Chr(39) + cSquare + Chr(39) + ')">'
 
 if !Empty(cContent)
    cHtml += '<span class="piece' + cColorClass + '">' + cContent + '</span>'
 endif
 
 if lIsValid .and. Empty(cContent)
     cHtml += '<div class="move-dot"></div>'
 endif

 cHtml += '</div>'
 
  next
   next

   cHtml += '</div>' // End chess-board (grid)
   cHtml += '</div>' // End board-container
   

   // 3. RIGHT GUTTER (Settings Icon)
   cHtml += '<div style="width: 30px; display: flex; flex-direction: column; height: 400px; padding-left: 5px; border-left: 0px solid transparent;">'
   // Icon aligned with Rank 8 (Top)
   cHtml += '<div style="height: 50px; display: flex; align-items: center; justify-content: center;">'
   cHtml += '<img src="board/setup.png" style="cursor:pointer; width:20px;" title="Impostazioni" onclick="openSettings()">'
   cHtml += '</div>'
   cHtml += '</div>'
   

   cHtml += '</div>' // End Center Flex Container
   

   // Bottom Coords (Letters) - Needs left padding to align with board
   cHtml += '<div style="display:flex; height: 20px; align-items: center; justify-content: flex-start; margin-left: 25px;">' // 20px Left Coords + 5px margin
   cHtml += '<div style="display:grid; grid-template-columns: repeat(8, 50px); justify-items: center;">'
   if lShowCoords
      for nFile := 1 to 8
          // If White: a..h. Black: h..a
          cContent := iif( lIsWhite, Chr(96+nFile), Chr(96+(9-nFile)) )
          cHtml += '<div class="coord-cell">' + cContent + '</div>'
      next
   endif
   cHtml += '</div>'
   cHtml += '</div>'
   

   cHtml += '</div>' // End chess-board-wrapper
   

   // Bottom Player
   cHtml += '<div class="player-info">'
   cHtml += '<img src="players/white_400.png" alt="White"><span>' + iif(hb_HHasKey(hConfig,"Player1"),hConfig["Player1"],"Giocatore 1") + '</span>'
   cHtml += '</div>'

   cHtml += '</div>' // End Container wrapper
return cHtml

function GetPieceSymbol( cPiece, cTheme )
   local cColor := iif( IsUpperLocal(cPiece), "w", "b" )
   local cType := Lower( cPiece ) // Always match filename logic: p, n, b, r, q, k
   local cFile
   

   if !Empty( cTheme ) .and. Upper(cTheme) != "DEFAULT"
       // Inline SVG Logic to support 'currentColor' and avoid 404 path issues
       // File: c:\hix\board\themes\<Theme>\<color><type>.svg
       cFile := "c:\hix\board\themes\" + cTheme + "\" + cColor + cType + ".svg"
       if File( cFile )
           return MemoRead( cFile )
       endif
   endif

   // Default Generic Glyphs
   do case
      case Upper(cPiece) == "P"
         return "♟" // User wanted simple PRG, using generic chess glyphs
      case Upper(cPiece) == "R"
         return "♜"
      case Upper(cPiece) == "N"
         return "♞"
      case Upper(cPiece) == "B"
         return "♝"
      case Upper(cPiece) == "Q"
         return "♛"
      case Upper(cPiece) == "K"
         return "♚"
   endcase
return cPiece

//----------------------------------------------------------------------------//
// View Helper (Standard HIX pattern)

function View( cName, hParams )
   local cData
   local cViewPath := "c:\hix\views\" + cName + ".view"
   if hParams == nil; hParams := {=>}; endif
   if File( cViewPath )
      cData = MemoRead( cViewPath )
      while ReplaceBlocks( @cData, "{{", "}}", hParams )
      end
   else
      cData = "<h2>" + cName + " not found!</h2>" 
   endif    

return cData

function ReplaceBlocks( cCode, cStartBlock, cEndBlock, hParams )
   local nStart, nEnd, cBlock, cParamName
   local lReplaced := .F.
   while ( nStart := At( cStartBlock, cCode ) ) != 0 .and. ( nEnd := At( cEndBlock, cCode ) ) != 0
      cBlock = SubStr( cCode, nStart + Len( cStartBlock ), nEnd - nStart - Len( cEndBlock ) )
      cParamName := AllTrim( cBlock )
      if hb_HHasKey( hParams, cParamName )
          cCode = SubStr( cCode, 1, nStart - 1 ) + ValToChar( hParams[ cParamName ] ) + SubStr( cCode, nEnd + Len( cEndBlock ) )
      else
          cCode = SubStr( cCode, 1, nStart - 1 ) + "" + SubStr( cCode, nEnd + Len( cEndBlock ) )
      endif
      lReplaced = .T.
   end
return lReplaced

//----------------------------------------------------------------------------//
// Configuration

function LoadSettings()
   local hConfig := {=>}
   local cIniFile := "c:\hix\chess.ini"
   

   // Defaults
   hConfig["SquareWhite"] := "#f0d9b5"
   hConfig["SquareBlack"] := "#b58863"
   hConfig["Background"]  := "default" // or 'wood'
   hConfig["ShowCoords"]  := .T.
   hConfig["CoordsMode"]  := "Normal"
   hConfig["Theme"]       := "Alpha"
   hConfig["TextColor"]   := "#000000"
   hConfig["Language"]    := "italian"
   

   if File( cIniFile )
      // Simple parsing assuming standard format
      // Note: In real Harbor use hb_IniRead, assuming manual for portability in this snippet
      hConfig["SquareWhite"] := GetIniVal( cIniFile, "config", "SquareWhite", hConfig["SquareWhite"] )
      hConfig["SquareBlack"] := GetIniVal( cIniFile, "config", "SquareBlack", hConfig["SquareBlack"] )
      hConfig["Background"]  := GetIniVal( cIniFile, "config", "Background", hConfig["Background"] )
      hConfig["ShowCoords"]  := ( GetIniVal( cIniFile, "config", "ShowCoords", "T" ) == "T" )
      hConfig["CoordsMode"]  := GetIniVal( cIniFile, "config", "CoordsMode", "Normal" )
      hConfig["Theme"]       := GetIniVal( cIniFile, "config", "Theme", hConfig["Theme"] )
    hConfig["TextColor"]   := GetIniVal( cIniFile, "config", "TextColor", hConfig["TextColor"] )
    hConfig["Language"]    := GetIniVal( cIniFile, "config", "Language", hConfig["Language"] )
    hConfig["Player1"]     := GetIniVal( cIniFile, "config", "Player1", "Giocatore 1" )
    hConfig["Player2"]     := GetIniVal( cIniFile, "config", "Player2", "Giocatore 2" )
    hConfig["Level"]       := GetIniVal( cIniFile, "config", "Level", "1" )
   endif
return hConfig

function SaveSettings( hConfig )
   local cIniFile := "c:\hix\chess.ini"
   local cData := "[config]" + hb_Eol()
   

   cData += "SquareWhite=" + hConfig["SquareWhite"] + hb_Eol()
   cData += "SquareBlack=" + hConfig["SquareBlack"] + hb_Eol()
   cData += "Background=" + hConfig["Background"] + hb_Eol()
   cData += "ShowCoords=" + iif( hConfig["ShowCoords"], "T", "F" ) + hb_Eol()
   cData += "CoordsMode=" + hConfig["CoordsMode"] + hb_Eol()
   cData += "Theme=" + hConfig["Theme"] + hb_Eol()
   cData += "TextColor=" + hConfig["TextColor"] + hb_Eol()
   cData += "Language=" + hConfig["Language"] + hb_Eol()
   

   // Persist Players & Level
   if hb_HHasKey(hConfig, "Player1"); cData += "Player1=" + hConfig["Player1"] + hb_Eol(); endif
   if hb_HHasKey(hConfig, "Player2"); cData += "Player2=" + hConfig["Player2"] + hb_Eol(); endif
   if hb_HHasKey(hConfig, "Level");   cData += "Level="   + hConfig["Level"]   + hb_Eol(); endif
   

   MemoWrit( cIniFile, cData )
return nil

function GetIniVal( cFile, cSection, cKey, cDefault )
   local cData := MemoRead( cFile )
   local aLines := hb_ATokens( cData, hb_Eol() )
   local i, cLine, nPos
   

   // Very basic parser
   for i := 1 to Len(aLines)
      cLine := AllTrim( aLines[i] )
      if Left(cLine, Len(cKey)+1) == cKey + "="
         return SubStr( cLine, Len(cKey)+2 )
      endif
   next
return cDefault

function ValToChar( u )
   local cType := ValType( u )
   if cType == "C"; return u; endif
   if cType == "N"; return AllTrim(Str(u)); endif
   if cType == "D"; return DtoS(u); endif
   if cType == "A"; return "{...}"; endif
   if cType == "O"; return "[Object]"; endif
   if cType == "L"; return iif(u, ".T.", ".F."); endif
   if cType == "U"; return "NIL"; endif
   return ""

function GetThemes()
   local aThemes := {}
   local aDir := Directory( "c:\hix\board\themes\*.*", "D" )
   local i
   

   for i := 1 to Len( aDir )
      if aDir[i][5] == "D" // Check Attribute if strictly directory, simpler:
      // Directory() returns array of subarrays. [1]=Name, [5]=Attr
      // "D" attribute check.
         if aDir[i][1] != "." .and. aDir[i][1] != ".."
            if IsDirectory( "c:\hix\board\themes\" + aDir[i][1] )
               AAdd( aThemes, aDir[i][1] )
            endif
         endif
      endif
   next
return aThemes

function GetThemesOptions( cSelected )
   local aThemes := GetThemes()
   local i, cHtml := '<option value="Default">Default (Glyphs)</option>'
   local cName, cSel
   

   for i := 1 to Len(aThemes)
      cName := aThemes[i]
      cSel := iif( Upper(cName) == Upper(cSelected), "selected", "" )
      cHtml += '<option value="' + cName + '" ' + cSel + '>' + cName + '</option>'
   next
return cHtml





function UseSavedGames()
   if File("c:\hix\mosse\saved.dbf")
      Use "c:\hix\mosse\saved.dbf" shared new alias "SAVED"
      return .T.
   endif
return .F.

//----------------------------------------------------------------------------//
// AI Logic

function GetComputerMove( cFEN, nLevel, cColor )
    local aMoves := GetAllLegalMoves( cFEN, cColor )
    local nBestScore := -9999
    local cBestMove := ""
    local cMove, cTarget, nScore, i
    local cMyLowColor := Lower(cColor) // 'w' or 'b'
    

if Len(aMoves) == 0; return ""; endif

// Level 1: Pure Random
if nLevel <= 1
   return aMoves[ HB_RandomInt( 1, Len(aMoves) ) ]
endif

// Level 2-9: Basic Evaluation (Material)
// Very simple: Try each move, check captured piece value
// Improve: Minimax for higher levels

for i := 1 to Len(aMoves)
    cMove := aMoves[i] // "e2e4"
    // Analyze target square content *before* move for simple capture logic
    // Or Simulate Move and Evaluate Board
    
    // Simple 1-ply capture logic
    cTarget := GetPieceAt( cFEN, Right(cMove, 2) ) 
    nScore := 0
    
    if !Empty( cTarget )
        // Capture!
        nScore += GetPieceValue( cTarget ) * 10
    endif
    
    // Add randomness for lower levels so it's not deterministic
    if nLevel < 5
       nScore += HB_RandomInt( 0, 5 )
    endif
    
    // Checkmate priority (Higher levels)
    // if nLevel >= 5 ... Simulate and check IsCheckmate
    
    if nScore > nBestScore
       nBestScore := nScore
       cBestMove := cMove
    elseif nScore == nBestScore
       // Randomly pick between equal moves
       if HB_RandomInt(0, 1) == 1
          cBestMove := cMove
       endif
    endif
next

return cBestMove

function GetAllLegalMoves( cFEN, cColor )
    local aAllMoves := {}
    local r, c, cSquare, cPiece
    local aMoves, i
    local aGrid := FENToGrid( cFEN )
    

for r := 1 to 8
   for c := 1 to 8
       cPiece := aGrid[r, c]
       if !Empty(cPiece) .and. IsOwnPiece( cPiece, cColor )
           cSquare := CoordsToSquare( r, c )
           aMoves := GetLegalMoves( cFEN, cSquare ) // Returns array of Target Squares
           for i := 1 to Len(aMoves)
               AAdd( aAllMoves, cSquare + aMoves[i] ) // "e2e4"
           next
       endif
   next
   next
return aAllMoves

function GetPieceValue( cPiece )
   local cType := Upper( cPiece )
   if cType == "P"; return 1; endif
   if cType == "N"; return 3; endif
   if cType == "B"; return 3; endif
   if cType == "R"; return 5; endif
   if cType == "Q"; return 9; endif
return 0

function IsUpperLocal( cChar )
   local nAsc := Asc( cChar )
   return nAsc >= 65 .and. nAsc <= 90

//----------------------------------------------------------------------------//
// Language Utilities

function LoadLanguage( cLang )
   local cFile := "c:\hix\lang\" + Lower(cLang) + ".lng"
   local hLang := {=>}
   local cLine, aTokens, cKey, cValue
   local nHandle, cContent
   

   // Default to Italian if file not found
   if !File( cFile )
      cFile := "c:\hix\lang\italian.lng"
   endif
   

   if !File( cFile )
      // Return empty hash if no language files exist
      return hLang
   endif
   

   // Read file line by line
   nHandle := FOpen( cFile, 0 ) // Read mode
   if nHandle != -1
      cContent := Space( 10000 )
      FRead( nHandle, @cContent, 10000 )
      FClose( nHandle )
      

  // Parse lines
  aTokens := hb_ATokens( cContent, Chr(13) + Chr(10) )
  for each cLine in aTokens
     cLine := AllTrim( cLine )
     
     // Skip comments and empty lines
     if Empty(cLine) .or. Left(cLine, 2) == "//"
        loop
     endif
     
     // Parse KEY=Value
     if "=" $ cLine
        cKey := AllTrim( SubStr( cLine, 1, At("=", cLine) - 1 ) )
        cValue := AllTrim( SubStr( cLine, At("=", cLine) + 1 ) )
        hLang[ cKey ] := cValue
     endif
  next
   endif
   

return hLang

function T( cKey, hLang )
   // Translation function - returns translated text or key if not found
   if hb_HHasKey( hLang, cKey )
      return hLang[ cKey ]
   endif
return cKey // Fallback to key itself

function GetLanguageOptions( cCurrentLang )
   local cHtml := ""
   local aLangs := { {"italian", "Italiano"}, {"english", "English"}, {"spanish", "Español"}, {"german", "Deutsch"} }
   local aLang
   

   for each aLang in aLangs
      cHtml += '<option value="' + aLang[1] + '"'
      if Lower(cCurrentLang) == aLang[1]
         cHtml += ' selected'
      endif
      cHtml += '>' + aLang[2] + '</option>'
   next
   

return cHtml
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX &amp; AntiGravity apps gallery
Posted: Thu Jan 01, 2026 06:56 PM

chess.ini

[config]
SquareWhite=#f0d9b5
SquareBlack=#b58863
Background=wood
ShowCoords=T
CoordsMode=Normal
Theme=Balestegui
TextColor=#5da4c4
Language=spanish
Player1=Silvio
Player2=Antonio
Level=0

make lang folder

italian.lng

// Italian Language File
// Format: KEY=Value

// Top Menu
NEW_GAME=Nuova Partita
LOAD_GAME=Carica Partita
SAVE_GAME=Salva Partita
EXIT=Uscita

// Settings Dialog
SETTINGS_TITLE=Impostazioni
PIECES=Pezzi
BOARD=Scacchiera
BOARD_DEFAULT=Grigio (Default)
BOARD_WOOD=Legno con nodi
SHOW_COORDS=Mostra coordinate scacchiera
COORDS_OUTSIDE=All'esterno
COORDS_FULL=Completa
PIECE_NOTATION=Notazione pezzi
ICONS=Icone
TEXT_COLOR=Colore Testo & Coordinate
CANCEL=Annulla
SAVE=Salva

// New Game Dialog
NEW_GAME_TITLE=Nuova Partita
GAME_TYPE=Tipo di Gioco
HUMAN_VS_HUMAN=Umano vs Umano
HUMAN_VS_PC=Umano vs PC
PLAYER1=Giocatore 1 (Tu)
PLAYER2=Giocatore 2
AI_LEVEL=Livello AI
YOUR_COLOR=Il tuo Colore
WHITE=Bianco
BLACK=Nero
START=Inizia

// AI Levels
LEVEL_1=Neofita
LEVEL_2=Principiante
LEVEL_3=Intermedio
LEVEL_4=Intermedio II
LEVEL_5=Avanzato
LEVEL_6=Esperto
LEVEL_7=Maestro
LEVEL_8=Grande Maestro
LEVEL_9=Massimo

// Save Game Dialog
SAVE_GAME_TITLE=Salva Partita
GAME_NAME=Nome Partita
ENTER_NAME=Inserisci nome...

// Load Game Dialog
LOAD_GAME_TITLE=Carica Partita
CLOSE=Chiudi

// Game Messages
CHECKMATE=SCACCO MATTO!
CHECK=SCACCO!
WINS=Vince!
COMPUTER_THINKING=Computer sta pensando...
ENTER_NAME_ALERT=Inserisci un nome!
GAME_SAVED=Partita Salvata
GAME_LOADED=Partita Caricata

// History
HISTORY=Cronologia

// Default Player Names
DEFAULT_PLAYER1=Giocatore 1
DEFAULT_PLAYER2=Giocatore 2
COMPUTER=Computer
LEVEL_SHORT=Liv.

spanish.lng

// Spanish Language File
// Format: KEY=Value

// Top Menu
NEW_GAME=Nueva Partida
LOAD_GAME=Cargar Partida
SAVE_GAME=Guardar Partida
EXIT=Salir

// Settings Dialog
SETTINGS_TITLE=Configuración
PIECES=Piezas
BOARD=Tablero
BOARD_DEFAULT=Gris (Predeterminado)
BOARD_WOOD=Madera con nudos
SHOW_COORDS=Mostrar coordenadas del tablero
COORDS_OUTSIDE=Exterior
COORDS_FULL=Completa
PIECE_NOTATION=Notación de piezas
ICONS=Iconos
TEXT_COLOR=Color de Texto y Coordenadas
CANCEL=Cancelar
SAVE=Guardar

// New Game Dialog
NEW_GAME_TITLE=Nueva Partida
GAME_TYPE=Tipo de Juego
HUMAN_VS_HUMAN=Humano vs Humano
HUMAN_VS_PC=Humano vs PC
PLAYER1=Jugador 1 (Tú)
PLAYER2=Jugador 2
AI_LEVEL=Nivel IA
YOUR_COLOR=Tu Color
WHITE=Blanco
BLACK=Negro
START=Comenzar

// AI Levels
LEVEL_1=Novato
LEVEL_2=Principiante
LEVEL_3=Intermedio
LEVEL_4=Intermedio II
LEVEL_5=Avanzado
LEVEL_6=Experto
LEVEL_7=Maestro
LEVEL_8=Gran Maestro
LEVEL_9=Máximo

// Save Game Dialog
SAVE_GAME_TITLE=Guardar Partida
GAME_NAME=Nombre de Partida
ENTER_NAME=Ingrese nombre...

// Load Game Dialog
LOAD_GAME_TITLE=Cargar Partida
CLOSE=Cerrar

// Game Messages
CHECKMATE=¡JAQUE MATE!
CHECK=¡JAQUE!
WINS=¡Gana!
COMPUTER_THINKING=Computadora pensando...
ENTER_NAME_ALERT=¡Ingrese un nombre!
GAME_SAVED=Partida Guardada
GAME_LOADED=Partida Cargada

// History
HISTORY=Historial

// Default Player Names
DEFAULT_PLAYER1=Jugador 1
DEFAULT_PLAYER2=Jugador 2
COMPUTER=Computadora
LEVEL_SHORT=Nv.

english.lng

// Spanish Language File
// Format: KEY=Value

// Top Menu
NEW_GAME=Nueva Partida
LOAD_GAME=Cargar Partida
SAVE_GAME=Guardar Partida
EXIT=Salir

// Settings Dialog
SETTINGS_TITLE=Configuración
PIECES=Piezas
BOARD=Tablero
BOARD_DEFAULT=Gris (Predeterminado)
BOARD_WOOD=Madera con nudos
SHOW_COORDS=Mostrar coordenadas del tablero
COORDS_OUTSIDE=Exterior
COORDS_FULL=Completa
PIECE_NOTATION=Notación de piezas
ICONS=Iconos
TEXT_COLOR=Color de Texto y Coordenadas
CANCEL=Cancelar
SAVE=Guardar

// New Game Dialog
NEW_GAME_TITLE=Nueva Partida
GAME_TYPE=Tipo de Juego
HUMAN_VS_HUMAN=Humano vs Humano
HUMAN_VS_PC=Humano vs PC
PLAYER1=Jugador 1 (Tú)
PLAYER2=Jugador 2
AI_LEVEL=Nivel IA
YOUR_COLOR=Tu Color
WHITE=Blanco
BLACK=Negro
START=Comenzar

// AI Levels
LEVEL_1=Novato
LEVEL_2=Principiante
LEVEL_3=Intermedio
LEVEL_4=Intermedio II
LEVEL_5=Avanzado
LEVEL_6=Experto
LEVEL_7=Maestro
LEVEL_8=Gran Maestro
LEVEL_9=Máximo

// Save Game Dialog
SAVE_GAME_TITLE=Guardar Partida
GAME_NAME=Nombre de Partida
ENTER_NAME=Ingrese nombre...

// Load Game Dialog
LOAD_GAME_TITLE=Cargar Partida
CLOSE=Cerrar

// Game Messages
CHECKMATE=¡JAQUE MATE!
CHECK=¡JAQUE!
WINS=¡Gana!
COMPUTER_THINKING=Computadora pensando...
ENTER_NAME_ALERT=¡Ingrese un nombre!
GAME_SAVED=Partida Guardada
GAME_LOADED=Partida Cargada

// History
HISTORY=Historial

// Default Player Names
DEFAULT_PLAYER1=Jugador 1
DEFAULT_PLAYER2=Jugador 2
COMPUTER=Computadora
LEVEL_SHORT=Nv.

german.lng

// German Language File
// Format: KEY=Value

// Top Menu
NEW_GAME=Neues Spiel
LOAD_GAME=Spiel Laden
SAVE_GAME=Spiel Speichern
EXIT=Beenden

// Settings Dialog
SETTINGS_TITLE=Einstellungen
PIECES=Figuren
BOARD=Schachbrett
BOARD_DEFAULT=Grau (Standard)
BOARD_WOOD=Holz mit Knoten
SHOW_COORDS=Brettkoordinaten anzeigen
COORDS_OUTSIDE=Außen
COORDS_FULL=Vollständig
PIECE_NOTATION=Figurennotation
ICONS=Symbole
TEXT_COLOR=Text- & Koordinatenfarbe
CANCEL=Abbrechen
SAVE=Speichern

// New Game Dialog
NEW_GAME_TITLE=Neues Spiel
GAME_TYPE=Spieltyp
HUMAN_VS_HUMAN=Mensch vs Mensch
HUMAN_VS_PC=Mensch vs PC
PLAYER1=Spieler 1 (Du)
PLAYER2=Spieler 2
AI_LEVEL=KI-Stufe
YOUR_COLOR=Deine Farbe
WHITE=Weiß
BLACK=Schwarz
START=Starten

// AI Levels
LEVEL_1=Anfänger
LEVEL_2=Einsteiger
LEVEL_3=Fortgeschritten
LEVEL_4=Fortgeschritten II
LEVEL_5=Erweitert
LEVEL_6=Experte
LEVEL_7=Meister
LEVEL_8=Großmeister
LEVEL_9=Maximum

// Save Game Dialog
SAVE_GAME_TITLE=Spiel Speichern
GAME_NAME=Spielname
ENTER_NAME=Namen eingeben...

// Load Game Dialog
LOAD_GAME_TITLE=Spiel Laden
CLOSE=Schließen

// Game Messages
CHECKMATE=SCHACHMATT!
CHECK=SCHACH!
WINS=Gewinnt!
COMPUTER_THINKING=Computer denkt nach...
ENTER_NAME_ALERT=Bitte Namen eingeben!
GAME_SAVED=Spiel Gespeichert
GAME_LOADED=Spiel Geladen

// History
HISTORY=Verlauf

// Default Player Names
DEFAULT_PLAYER1=Spieler 1
DEFAULT_PLAYER2=Spieler 2
COMPUTER=Computer
LEVEL_SHORT=St.

Make bitmaps folder

and insert top.png

create players folder and insert these two files

then there are many file svg as I told to antonio I cannot insert here

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX &amp; AntiGravity apps gallery
Posted: Thu Jan 01, 2026 06:58 PM

chess.view

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hix Chess</title>
  {{View("style")}}
  <style>
    .chess-container {
        display: none; /* Hidden strictly logic until splash done (handled by js) */
        flex-direction: column;
        align-items: center;
        margin-top: 0px; 
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
        width: 100%;
        height: 100vh;
        background-color: #e0e0e0; 
    }
    

#splash-screen {
    position: fixed;
    top: 0; left: 0; width: 100%; height: 100%;
    background-color: #2d2d2d;
    display: none; /* Default to hidden to prevent flash, JS or Conditional will show/hide */
    justify-content: center;
    align-items: center;
    z-index: 9999;
}
#splash-screen img {
    max-width: 80%;
    max-height: 80%;
    box-shadow: 0 0 50px rgba(0,0,0,0.8);
}

.button-bar {
    width: 100%;
    height: 70px;
    background-color: #3e2723;
    display: flex;
    align-items: center;
    justify-content: flex-start; /* Left aligned */
    padding: 0 20px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    margin-bottom: 20px;
}

.btn-bar {
    padding: 8px 15px;
    background-color: #5d4037;
    color: white;
    border: 1px solid #795548;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    margin-left: 10px;
}
.btn-bar:hover {
    background-color: #4e342e;
}

/* Wrapper styling reduced, mostly handled by PRG structure */
/* Wrapper is now the Wood Frame */
.chess-board-wrapper {
    display: inline-block; 
    background-color: #8b4513; /* Wood fallback */
    padding: 15px; /* Spacing for coords inside frame */
    border-radius: 4px; 
    box-shadow: 0 5px 15px rgba(0,0,0,0.5); 
    position: relative;
    border: 2px solid #5d4037;
}

.wood-bg {
    background: repeating-linear-gradient(45deg, #8b4513 0, #8b4513 10px, #a0522d 10px, #a0522d 20px) !important;
}

.player-info {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 5px 0;
    color: #333; /* Fallback */
    font-weight: bold;
    width: 100%; /* Ensure it spans */
}

.player-info img {
    width: 30px; 
    height: 30px;
    border-radius: 4px;
    background-color: rgba(255,255,255,0.2);
}

.board-container {
    display: flex;
    flex-direction: row;
    border: none; /* Border handled by chess-board or logic */
}

.coord-cell {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 12px;
    font-weight: bold;
    color: #555;
}

.chess-board {
    display: grid;
    grid-template-columns: repeat(8, 50px);
    grid-template-rows: repeat(8, 50px);
    width: 400px;
    height: 400px;
    border: 2px solid #4a3021;
    user-select: none;
}

.square {
    width: 50px;
    height: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 35px; /* Adjust piece glyph size if needed, though SVG is 100% */
    position: relative;
    cursor: default;
}

.white-square {
    background-color: #f0d9b5;
}

.black-square {
    background-color: #b58863;
}

.selected {
    background-color: #f5f682 !important;
}

.valid-move {
    cursor: pointer;
}

.move-dot {
    width: 20px;
    height: 20px;
    background-color: rgba(0, 0, 0, 0.2);
    border-radius: 50%;
    position: absolute;
    pointer-events: none;
}

.square:hover .move-dot {
    background-color: rgba(0, 0, 0, 0.4);
}

/* Piece Styles */
.piece {
    font-size: 40px;
    cursor: pointer;
    z-index: 10;
    /* Prevent text selection */
    -webkit-user-select: none;
    user-select: none;
}

.piece-white {
    color: #fff;
    text-shadow: 1px 1px 2px #000;
}

.piece-black {
    color: #000;
    text-shadow: 0px 0px 1px #fff; /* Slight halo for visibility on dark */
}

.controls {
    margin-top: 20px;
    display: flex;
    gap: 15px;
    align-items: center;
}

.btn {
    padding: 10px 20px;
    background-color: #5d4037;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
}

.btn:hover {
    background-color: #3e2723;
}

.status-panel {
    font-size: 20px;
    font-weight: bold;
    margin-bottom: 10px;
    display: none; /* Hidden as per redesign */
}

.settings-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #4e342e; /* "Marroncino" / Dark Brown to match theme */
    padding: 0;
    border-radius: 8px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.5);
    z-index: 1000;
    width: 400px;
    font-family: "Segoe UI", sans-serif;
    color: #e0e0e0;
    border: 1px solid #5d4037;
    display: none; /* Hidden by default */
}
    color: #333;
}

.turn-indicator {
    margin-left: 10px;
    padding: 5px 10px;
    background: #eee;
    border-radius: 4px;
}


.wood-bg {
    background-color: #8b4513; 
    /* CSS Wood Texture */
    background: repeating-linear-gradient(45deg, #a0522d 0, #a0522d 10px, #8b4513 10px, #8b4513 20px);
    border: 4px solid #5d4037;
    box-shadow: inset 0 0 20px #3e2723;
}



.settings-header {
    width: 100%;
    height: 170px; /* Increased height to show more of the image */
    background-image: url('bitmaps/top.png');
    background-size: cover;
    background-position: top; /* Show from the top of the image */
    border-top-left-radius: 8px;
    border-top-right-radius: 8px;
}

.settings-content {
    padding: 20px;
    padding-top: 10px; /* Reduced top padding since header is larger */
}

.form-group {
    margin-bottom: 15px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.form-group label {
    font-size: 14px;
    color: #bbb;
}

.form-control {
    background: #3d3d3d;
    border: 1px solid #555;
    color: white;
    padding: 8px;
    border-radius: 4px;
    width: 180px;
}

/* Switch Styles */
.switch {
  position: relative;
  display: inline-block;
  width: 40px;
  height: 20px;
}
.switch input { opacity: 0; width: 0; height: 0; }
.slider {
  position: absolute;
  cursor: pointer;
  top: 0; left: 0; right: 0; bottom: 0;
  background-color: #555;
  transition: .4s;
  border-radius: 20px;
}
.slider:before {
  position: absolute;
  content: "";
  height: 16px;
  width: 16px;
  left: 2px;
  bottom: 2px;
  background-color: white;
  transition: .4s;
  border-radius: 50%;
}
input:checked + .slider {
  background-color: #4CAF50;
}
input:checked + .slider:before {
  transform: translateX(20px);
}

.modal-actions {
    display: flex;
    justify-content: space-between;
    padding: 15px 20px; /* Padding for footer */
    background: #252525;
    border-bottom-left-radius: 8px;
    border-bottom-right-radius: 8px;
}

.btn-cancel {
    background: #444;
    color: white;
    padding: 8px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    width: 45%;
}

.btn-save {
    background: #4CAF50; /* Green */
    color: white;
    padding: 8px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    width: 45%;
}

.coords-options {
    margin-top: 10px;
    background: #333;
    padding: 10px;
    border-radius: 4px;
}
.coords-radio {
    display: flex;
    gap: 10px;
    font-size: 13px;
    color: #aaa;
}
    margin-top: 20px;
}

  </style>
</head>
<body>
  <!-- Splash Screen -->
  <!-- Check if action is active. If so, FORCE style='display:none !important' -->
  <div id="splash-screen" style="display: {{splash_style}};">
      <img src="bitmaps/top.png" alt="HIX Chess">
  </div>

  <div class="chess-container" id="main-container">
    

<!-- Top Button Bar -->
<div class="button-bar">
    <!-- Moved Game Controls Here -->
    <button type="button" class="btn-bar" onclick="newGame()">{{L_NEW_GAME}}</button>
    <button type="button" class="btn-bar" onclick="openLoadGame()">{{L_LOAD_GAME}}</button>
    <button type="button" class="btn-bar" onclick="openSaveGame()">{{L_SAVE_GAME}}</button>
    <div style="flex-grow: 1;"></div> <!-- Spacer -->
    <button type="button" class="btn-bar" onclick="window.close()">{{L_EXIT}}</button>
</div>

<!-- Removed Header and Status Panel -->

<div style="display: flex; gap: 20px;">
    <div>
        <form id="chessForm" action="chess.prg" method="POST">
            <input type="hidden" name="action" id="action" value="">
            <input type="hidden" name="fen" id="fen" value="{{fen}}">
            <input type="hidden" name="selected" id="selected" value="{{selected}}">
            <input type="hidden" name="clicked_square" id="clicked_square" value="">
            <input type="hidden" name="game_id" id="game_id" value="{{game_id}}">
            

        <!-- Settings Fields (Hidden) -->
        <input type="hidden" name="square_white" id="h_square_white">
        <input type="hidden" name="square_black" id="h_square_black">
        <input type="hidden" name="background" id="h_background">
        <input type="hidden" name="theme" id="h_theme">
        <input type="hidden" name="text_color" id="h_text_color">
        <input type="hidden" name="show_coords" id="h_show_coords">
        <input type="hidden" name="coords_mode" id="h_coords_mode">
        
        <!-- Game State Persistence -->
        <input type="hidden" name="player1" value="{{player1}}">
        <input type="hidden" name="player2" value="{{player2}}">
        <input type="hidden" name="game_mode" value="{{game_mode}}">
        <input type="hidden" name="level" value="{{level}}">
        <input type="hidden" name="my_color" value="{{my_color}}">
        
        <!-- Board injected from PRG -->
        {{board_html}}
        
        
    </form>
</div>

<div class="moves-box" style="width: 250px; height: 480px; overflow-y: auto; border: 4px solid #8b4513; padding: 10px; background: #fffdf0; border-radius: 4px; box-shadow: 0 5px 15px rgba(0,0,0,0.5);">
    <h3 style="margin-top:0; color:#5d4037; border-bottom: 2px solid #8b4513; padding-bottom:5px;">{{L_HISTORY}}</h3>
    {{moves_list}}
</div>
</div>

  </div>

<!-- Rewritten Settings Dialog -->
<div id="settingsModal" class="settings-modal">
    <div class="settings-header"></div> <!-- Images via CSS -->
    

<form id="settingsForm" method="POST" action="chess.prg">
    <input type="hidden" name="action" id="action_settings" value="save_settings">
    
    <!-- Hidden inputs -->
    <input type="hidden" name="square_white" id="h_square_white_dlg" value="{{cfg_white}}">
    <input type="hidden" name="square_black" id="h_square_black_dlg" value="{{cfg_black}}">
    <input type="hidden" name="background" id="h_background_dlg">
    <input type="hidden" name="theme" id="h_theme_dlg">
    <input type="hidden" name="show_coords" id="h_show_coords_dlg">
    <input type="hidden" name="coords_mode" id="h_coords_mode_dlg">
    <input type="hidden" name="language" id="h_language_dlg">
    <input type="hidden" name="text_color" id="h_text_color_dlg" value="{{cfg_text}}">

    <div class="settings-content">
        
        <div class="form-group">
             <label>{{L_PIECES}}</label>
             <select id="dlg_theme" class="form-control">
                 {{theme_options}}
             </select>
        </div>
        
        <div class="form-group">
            <label>{{L_BOARD}}</label>
            <select id="dlg_background" class="form-control">
                <option value="default">{{L_BOARD_DEFAULT}}</option>
                <option value="wood">{{L_BOARD_WOOD}}</option>
            </select>
        </div>

        <div class="form-group">
            <label>{{L_SHOW_COORDS}}</label>
            <label class="switch">
                <input type="checkbox" id="dlg_show_coords" onchange="toggleCoordsOptions()" {{cfg_coord_chk}}>
                <span class="slider"></span>
            </label>
        </div>

        <div id="coords_options_panel" class="coords-options" style="display: none;">
            <div class="coords-radio">
                 <label><input type="radio" name="dlg_coords_mode" value="Normal" {{chk_normal}}> {{L_COORDS_OUTSIDE}}</label>
                 <label><input type="radio" name="dlg_coords_mode" value="Full" {{chk_full}}> {{L_COORDS_FULL}}</label>
            </div>
        </div>
        
        <!-- Notazione Pezzi (Placeholder) -->
         <div class="form-group">
            <label>{{L_PIECE_NOTATION}}</label>
            <select class="form-control" disabled>
                <option>{{L_ICONS}}</option>
            </select>
        </div>
        
        <div class="form-group">
            <label>{{L_TEXT_COLOR}}</label>
            <input type="color" id="dlg_text_color" class="form-control" value="{{cfg_text}}" style="height:40px; padding:0;">
        </div>
        
        <div class="form-group">
            <label>{{L_LANGUAGE}}</label>
            <select id="dlg_language" class="form-control">
                {{language_options}}
            </select>
        </div>

    </div>
    
    <div class="modal-actions">
        <button type="button" class="btn-cancel" onclick="closeSettings()">{{L_CANCEL}}</button>
        <button type="button" class="btn-save" onclick="saveSettings()">{{L_SAVE}}</button>
    </div>
</form>
</div>

<!-- New Game Modal -->
<div id="newGameModal" class="settings-modal" style="display:none;">
    <div class="settings-header" style="height: 60px; background:none; background-color:#3e2723; display:flex; align-items:center; justify-content:center;">
        <h3 style="margin:0; color:white;">{{L_NEW_GAME_TITLE}}</h3>
    </div>
    

<div class="settings-content">
    <!-- Game Type -->
    <div class="form-group">
        <label>{{L_GAME_TYPE}}</label>
        <div style="display:flex; gap:10px;">
            <label><input type="radio" name="ng_mode" value="HvH" checked onchange="toggleNewGameOptions()"> {{L_HUMAN_VS_HUMAN}}</label>
            <label><input type="radio" name="ng_mode" value="HvPC" onchange="toggleNewGameOptions()"> {{L_HUMAN_VS_PC}}</label>
        </div>
    </div>

    <!-- Player 1 -->
    <div class="form-group">
        <label>{{L_PLAYER1}}</label>
        <input type="text" id="ng_player1" class="form-control" value="Giocatore 1">
    </div>

    <!-- Player 2 (HvH) -->
    <div class="form-group" id="grp_player2">
        <label>{{L_PLAYER2}}</label>
        <input type="text" id="ng_player2" class="form-control" value="Giocatore 2">
    </div>

    <!-- Level (HvPC) -->
    <div class="form-group" id="grp_level" style="display:none;">
        <label>{{L_AI_LEVEL}}</label>
        <select id="ng_level" class="form-control">
            <option value="1">1 - {{L_LEVEL_1}}</option>
            <option value="2">2 - {{L_LEVEL_2}}</option>
            <option value="3">3 - {{L_LEVEL_3}}</option>
            <option value="4">4 - {{L_LEVEL_4}}</option>
            <option value="5">5 - {{L_LEVEL_5}}</option>
            <option value="6">6 - {{L_LEVEL_6}}</option>
            <option value="7">7 - {{L_LEVEL_7}}</option>
            <option value="8">8 - {{L_LEVEL_8}}</option>
            <option value="9">9 - {{L_LEVEL_9}}</option>
        </select>
    </div>

    <!-- Color -->
    <div class="form-group">
        <label>{{L_YOUR_COLOR}}</label>
        <select id="ng_color" class="form-control">
            <option value="White">{{L_WHITE}}</option>
            <option value="Black">{{L_BLACK}}</option>
        </select>
    </div>
</div>

<div class="modal-actions">
    <button type="button" class="btn-cancel" onclick="closeNewGame()">{{L_CANCEL}}</button>
    <button type="button" class="btn-save" onclick="startNewGame()">{{L_START}}</button>
</div>

<!-- Hidden form for new game submission -->
<form id="newGameForm" method="POST" action="chess.prg" style="display:none;">
    <input type="hidden" name="action" value="new_game_start">
    <input type="hidden" name="game_mode" id="h_ng_mode">
    <input type="hidden" name="player1" id="h_ng_player1">
    <input type="hidden" name="player2" id="h_ng_player2">
    <input type="hidden" name="level" id="h_ng_level">
    <input type="hidden" name="my_color" id="h_ng_color">
    <!-- Pass current settings too -->
    <input type="hidden" name="theme" value="{{cfg_theme}}">
    <input type="hidden" name="background" value="{{cfg_bg}}">
    <input type="hidden" name="show_coords" value="{{cfg_coord_chk}}">
</form>
</div>


<!-- Save Game Modal -->
<div id="saveGameModal" class="settings-modal" style="display:none;">
    <div class="settings-header" style="height: 60px; background:none; background-color:#3e2723; display:flex; align-items:center; justify-content:center;">
        <h3 style="margin:0; color:white;">{{L_SAVE_GAME_TITLE}}</h3>
    </div>
    <div class="settings-content">
        <div class="form-group">
            <label>{{L_GAME_NAME}}</label>
            <input type="text" id="save_name" class="form-control" placeholder="{{L_ENTER_NAME}}">
        </div>
    </div>
    <div class="modal-actions">
        <button type="button" class="btn-cancel" onclick="document.getElementById('saveGameModal').style.display='none'">{{L_CANCEL}}</button>
        <button type="button" class="btn-save" onclick="submitSaveGame()">{{L_SAVE}}</button>
    </div>
</div>

<!-- Load Game Modal -->
<div id="loadGameModal" class="settings-modal" style="display:none;">
    <div class="settings-header" style="height: 60px; background:none; background-color:#3e2723; display:flex; align-items:center; justify-content:center;">
        <h3 style="margin:0; color:white;">{{L_LOAD_GAME_TITLE}}</h3>
    </div>
    <div class="settings-content" style="max-height: 300px; overflow-y: auto;">
        <ul id="game_list_ul" style="list-style: none; padding: 0; margin: 0;">
            {{saved_games_list}}
        </ul>
    </div>
    <div class="modal-actions">
        <button type="button" class="btn-cancel" onclick="document.getElementById('loadGameModal').style.display='none'">{{L_CLOSE}}</button>
    </div>
</div>

  <script>
    function selectSquare(squareId) {
        document.getElementById('action').value = 'select';
        document.getElementById('clicked_square').value = squareId;
        document.getElementById('chessForm').submit();
    }

function newGame() {
    document.getElementById('action').value = 'new';
    document.getElementById('chessForm').submit();
}

// Initialize Dialog
function openSettings() {
    document.getElementById('settingsModal').style.display = 'block';
    

// Set Background Select
let bgVal = document.getElementById('h_background_dlg').value || 'default';
document.getElementById('dlg_background').value = bgVal;

toggleCoordsOptions();
}

function closeSettings() {
    document.getElementById('settingsModal').style.display = 'none';
}

function toggleCoordsOptions() {
    let chk = document.getElementById('dlg_show_coords').checked;
    let panel = document.getElementById('coords_options_panel');
    panel.style.display = chk ? 'block' : 'none';
}

function saveSettings() {
    // Hidden inputs are in settingsForm, IDs end with _dlg
    document.getElementById('h_background_dlg').value = document.getElementById('dlg_background').value;
    document.getElementById('h_theme_dlg').value = document.getElementById('dlg_theme').value;
    document.getElementById('h_text_color_dlg').value = document.getElementById('dlg_text_color').value;
    document.getElementById('h_language_dlg').value = document.getElementById('dlg_language').value;
    

// Checkbox handling logic fixed:
if(document.getElementById('dlg_show_coords').checked) {
    document.getElementById('h_show_coords_dlg').name = "show_coords"; 
    document.getElementById('h_show_coords_dlg').value = "1";
} else {
    document.getElementById('h_show_coords_dlg').removeAttribute("name");
}

document.getElementById('action_settings').value = 'save_settings';

// Copy Coords Mode radio
let radios = document.getElementsByName('dlg_coords_mode');
for(let r of radios) { if(r.checked) document.getElementById('h_coords_mode_dlg').value = r.value; }

document.getElementById('settingsForm').submit();
}

// New Game Modal Logic
function newGame() {
    document.getElementById('newGameModal').style.display = 'block';
    toggleNewGameOptions();
}

// AI Auto-Move Logic
var isComputerThinking = "{{computer_thinking}}" === "Y";
if ( isComputerThinking ) {
    // Show some indicator?
    document.title = "{{L_COMPUTER_THINKING}}";
    setTimeout(function() {
        document.getElementById('action').value = 'computer_move';
        document.getElementById('chessForm').submit();
    }, 100); // 100ms delay, fast response
}

function closeNewGame() {
    document.getElementById('newGameModal').style.display = 'none';
}

function toggleNewGameOptions() {
    let mode = document.querySelector('input[name="ng_mode"]:checked').value;
    if(mode === 'HvPC') {
        document.getElementById('grp_player2').style.display = 'none';
        document.getElementById('grp_level').style.display = 'flex';
    } else {
        document.getElementById('grp_player2').style.display = 'flex';
        document.getElementById('grp_level').style.display = 'none';
    }
}

function startNewGame() {
    let mode = document.querySelector('input[name="ng_mode"]:checked').value;
    document.getElementById('h_ng_mode').value = mode;
    document.getElementById('h_ng_player1').value = document.getElementById('ng_player1').value;
    

if(mode === 'HvPC') {
    let level = document.getElementById('ng_level').value;
    document.getElementById('h_ng_level').value = level;
    document.getElementById('h_ng_player2').value = "Computer (Liv. " + level + ")";
} else {
    document.getElementById('h_ng_player2').value = document.getElementById('ng_player2').value;
}

document.getElementById('h_ng_color').value = document.getElementById('ng_color').value;

document.getElementById('newGameForm').submit();
}

function openSaveGame() {
    document.getElementById('saveGameModal').style.display = 'block';
}

function submitSaveGame() {
    let name = document.getElementById('save_name').value;
    if(!name) { alert("{{L_ENTER_NAME_ALERT}}"); return; }
    

// Use the main form to submit action
document.getElementById('action').value = 'save_game';
// Create a hidden input for save_name dynamically
let input = document.createElement("input");
input.type = "hidden";
input.name = "save_name";
input.value = name;
document.getElementById('chessForm').appendChild(input);

document.getElementById('chessForm').submit();
}

function openLoadGame() {
   // Trigger backend to fetch list
   document.getElementById('action').value = 'load_game_list';
   document.getElementById('chessForm').submit();
}

function loadGame( id ) {
    document.getElementById('action').value = 'load_game';
    document.getElementById('game_id').value = id;
    document.getElementById('chessForm').submit();
}



// Splash Screen Logic
window.onload = function() {
    // Check if we are in "new" or "reload".
    // Simple logic: if sessionStorage has flag, skip.
    // User requested: "at the beginning".
    


// Check if there was an action (move, select, save, etc.). If so, DO NOT show splash.
var hasAction = "{{has_active_action}}";

if (hasAction === "Y" || sessionStorage.getItem('splashShown')) {
    document.getElementById('splash-screen').style.display = 'none';
    document.getElementById('main-container').style.display = 'flex';
    sessionStorage.setItem('splashShown', 'true');
} else {
    setTimeout(function() {
        document.getElementById('splash-screen').style.opacity = 0;
        document.getElementById('splash-screen').style.transition = "opacity 1s";
        setTimeout(function(){ 
            document.getElementById('splash-screen').style.display = 'none';
            document.getElementById('main-container').style.display = 'flex';
        }, 1000);
        sessionStorage.setItem('splashShown', 'true');
    }, 1500);
}

// Check if we need to auto-open Load Modal (e.g. after list_games action)
if( "{{open_load_modal}}" === "Y" ) {
    openLoadGame();
}

var msg = "{{status_msg}}";
if (msg && msg.indexOf("Saved") !== -1) alert(msg);
};
</script>
</body>
</html>
```

style.view

<style>
  body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    padding: 20px;
    color: #333;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .container {
    background: white;
    border-radius: 20px;
    padding: 30px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    width: 350px; /* Fixed width for calculator */
  }

  h1 {
    color: #333;
    margin-bottom: 20px;
    font-size: 24px;
    text-align: right;
    font-weight: 300;
  }

  .calculator-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 12px;
  }

  .calc-display {
    grid-column: 1 / -1;
    background-color: #f8f9fa;
    color: #333;
    padding: 20px;
    border-radius: 10px;
    font-size: 2rem;
    text-align: right;
    border: none;
    margin-bottom: 20px;
    box-shadow: inset 0 2px 5px rgba(0,0,0,0.05);
  }

  .calc-btn {
    border: none;
    background-color: #f0f0f0;
    font-size: 1.25rem;
    color: #333;
    padding: 20px;
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.2s;
    user-select: none;
  }

  .calc-btn:hover {
    background-color: #e0e0e0;
  }

  .calc-btn.operator {
    background-color: #e0e0e0;
    color: #333;
    font-weight: bold;
    font-size: 1.4rem; /* Larger font for operators */
  }
  

  .calc-btn.operator:hover {
    background-color: #d0d0d0;
  }

  .calc-btn.equals {
    grid-column: span 2;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
  }
  

  .calc-btn.equals:hover {
    opacity: 0.9;
  }
  

  .calc-btn.clear {
    background-color: #ffebee;
    color: #d32f2f;
  }
  

  .calc-btn.clear:hover {
    background-color: #ffcdd2;
  }

  /* Hide actual form elements */
  .hidden-form {
    display: none;
  }
  

  /* Result specific */
  .result-box {
    background: #f8f9fa;
    border-radius: 8px;
    padding: 20px;
    text-align: center;
    margin-bottom: 20px;
  }

  .result-value {
    font-size: 36px;
    color: #667eea;
    font-weight: bold;
    margin-top: 10px;
  }
</style>
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX &amp; AntiGravity apps gallery
Posted: Thu Jan 01, 2026 07:00 PM

create "Mosse" folder here the procedure create a dbf to save all moves

create board folder and insert this png

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX &amp; AntiGravity apps gallery
Posted: Thu Jan 01, 2026 07:06 PM

on board folder create the folder "themes"
on themes folder create "Alpha" and "Balestegui" folders

download from here
https://github.com/FiveTechSoft/FWH_tools/blob/master/themes.rar

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 1283
Joined: Fri Feb 10, 2006 02:34 PM
Re: HIX &amp; AntiGravity apps gallery
Posted: Thu Jan 01, 2026 07:09 PM

Silvio,

Truly impressive. For me, the important thing is that the tool helps everyone create their own applications. With these examples created in just one day, I'm satisfied.

Challenge: Player 2 (AI) Use CURL... :wink:

Congratulations.

C.

Salutacions, saludos, regards

"...programar es fácil, hacer programas es difícil..."

UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
HIX -> https://github.com/carles9000/hix
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: HIX &amp; AntiGravity apps gallery
Posted: Fri Jan 02, 2026 10:19 PM
Carles wrote:

Silvio,

Truly impressive. For me, the important thing is that the tool helps everyone create their own applications. With these examples created in just one day, I'm satisfied.

Challenge: Player 2 (AI) Use CURL... :wink:

Congratulations.

C.

thanks C.

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com

Continue the discussion