HIX & AntiGravity apps gallery

csincuir
Posts: 483
Joined: Sat Feb 03, 2007 6:36 am
Location: Guatemala
Contact:

Re: HIX & AntiGravity apps gallery

Post by csincuir »

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

Carlos

User avatar
acuellar
Posts: 1713
Joined: Tue Oct 28, 2008 6:26 pm
Location: Santa Cruz-Bolivia

Re: HIX & AntiGravity apps gallery

Post by acuellar »

Antonio Linares wrote: Wed Dec 24, 2025 9:00 am

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 Cuéllar

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: HIX & AntiGravity apps gallery

Post by Antonio Linares »

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
User avatar
acuellar
Posts: 1713
Joined: Tue Oct 28, 2008 6:26 pm
Location: Santa Cruz-Bolivia

Re: HIX & AntiGravity apps gallery

Post by acuellar »

Perfecto.

Espero para mañana

Gracias.

Saludos,

Adhemar Cuéllar

User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

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

Image

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
User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

chess.prg

Code: Select all | Expand

// 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 "&#9823;" // User wanted simple PRG, using generic chess glyphs case Upper(cPiece) == "R" return "&#9820;" case Upper(cPiece) == "N" return "&#9822;" case Upper(cPiece) == "B" return "&#9821;" case Upper(cPiece) == "Q" return "&#9819;" case Upper(cPiece) == "K" return "&#9818;" 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
User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

chess.ini

Code: Select all | Expand

[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

Code: Select all | Expand

// 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

Code: Select all | Expand

// 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

Code: Select all | Expand

// 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

Code: Select all | Expand

// 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

Image

create players folder and insert these two files

Image

Image

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
User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

chess.view

Code: Select all | Expand

<!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

Code: Select all | Expand

<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
User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

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

create board folder and insert this png

Image

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
User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

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

download from here
https://github.com/FiveTechSoft/FWH_too ... themes.rar

Last edited by Silvio.Falconi on Sat Jan 03, 2026 11:44 am, edited 2 times in total.
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
User avatar
Carles
Posts: 1298
Joined: Fri Feb 10, 2006 2:34 pm
Location: Barcelona
Contact:

Re: HIX & AntiGravity apps gallery

Post by Carles »

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
User avatar
Silvio.Falconi
Posts: 7348
Joined: Thu Oct 18, 2012 7:17 pm

Re: HIX & AntiGravity apps gallery

Post by Silvio.Falconi »

Carles wrote: Thu Jan 01, 2026 7: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.

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
Post Reply