diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 5da610d01..ea26f2b6f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -169,13 +169,13 @@ import com.vitorpamplona.quartz.nip58Badges.BadgeProfilesEvent import com.vitorpamplona.quartz.nip59Giftwrap.WrappedEvent import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessDrawOfferEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameAcceptEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessMoveEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.draw.LiveChessDrawOfferEvent +import com.vitorpamplona.quartz.nip64Chess.accept.LiveChessGameAcceptEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt index 143f6ce8b..82a966095 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip64Chess/ChessAction.kt @@ -24,10 +24,10 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip64Chess.Color import com.vitorpamplona.quartz.nip64Chess.GameResult import com.vitorpamplona.quartz.nip64Chess.GameTermination -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameAcceptEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessMoveEvent +import com.vitorpamplona.quartz.nip64Chess.accept.LiveChessGameAcceptEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent /** * Action class for creating and signing live chess events diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/chess/ChessTopNavFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/chess/ChessTopNavFilter.kt index de820177d..597005256 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/chess/ChessTopNavFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/topNavFeeds/chess/ChessTopNavFilter.kt @@ -26,9 +26,9 @@ import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 9a95f5a55..4fd832695 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -217,9 +217,9 @@ import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetup import com.vitorpamplona.quartz.nip58Badges.BadgeAwardEvent import com.vitorpamplona.quartz.nip58Badges.BadgeDefinitionEvent -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt index ab22901ef..4c1a9da8f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Chess.kt @@ -52,9 +52,9 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew import com.vitorpamplona.amethyst.ui.stringRes -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import com.vitorpamplona.quartz.nip64Chess.Color as ChessColor /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/AndroidChessAdapter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/AndroidChessAdapter.kt index 18c639ec4..5a532819b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/AndroidChessAdapter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/AndroidChessAdapter.kt @@ -34,10 +34,10 @@ import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.quartz.nip64Chess.ChessGameEnd import com.vitorpamplona.quartz.nip64Chess.ChessMoveEvent import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.JesterGameEvents -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol -import com.vitorpamplona.quartz.nip64Chess.toJesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent /** * Android implementation of ChessEventPublisher using Jester protocol. diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt index 7c7fef8b7..2e30e791a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt @@ -33,9 +33,9 @@ import com.vitorpamplona.amethyst.commons.chess.PublicGame import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState -import com.vitorpamplona.quartz.nip64Chess.toJesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent import kotlinx.coroutines.flow.StateFlow /** diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt index 26749dd38..883704540 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/dal/ChessFeedFilter.kt @@ -26,7 +26,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder import com.vitorpamplona.amethyst.ui.dal.FilterByListParams -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent /** * Feed filter for NIP-64 Chess Game events (Kind 64) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt index fcf5941dc..864c5b194 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/dal/HomeNewThreadFeedFilter.kt @@ -39,9 +39,9 @@ import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip64Chess/FilterHomePostsByChess.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip64Chess/FilterHomePostsByChess.kt index 3999badfc..dbde0f4c4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip64Chess/FilterHomePostsByChess.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip64Chess/FilterHomePostsByChess.kt @@ -24,9 +24,9 @@ import com.vitorpamplona.amethyst.model.topNavFeeds.chess.ChessTopNavPerRelayFil import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent val ChessPostsKinds = listOf( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip65Follows/FilterHomePostsByAuthors.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip65Follows/FilterHomePostsByAuthors.kt index fb9ca719e..7d80db5b1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip65Follows/FilterHomePostsByAuthors.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/datasource/nip65Follows/FilterHomePostsByAuthors.kt @@ -39,9 +39,9 @@ import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt index 375f0dd12..19872675d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationScreen.kt @@ -221,13 +221,13 @@ import com.vitorpamplona.quartz.nip58Badges.BadgeProfilesEvent import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessDrawOfferEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameAcceptEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessMoveEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.draw.LiveChessDrawOfferEvent +import com.vitorpamplona.quartz.nip64Chess.accept.LiveChessGameAcceptEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip66RelayMonitor.discovery.RelayDiscoveryEvent import com.vitorpamplona.quartz.nip66RelayMonitor.monitor.RelayMonitorEvent diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventCollector.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventCollector.kt index c15195dbf..8a4575f3f 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventCollector.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventCollector.kt @@ -21,10 +21,10 @@ package com.vitorpamplona.amethyst.commons.chess import com.vitorpamplona.quartz.nip01Core.core.Event -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.JesterGameEvents -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol -import com.vitorpamplona.quartz.nip64Chess.toJesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessGameLoader.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessGameLoader.kt index f0557e2ab..0b4212e3e 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessGameLoader.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessGameLoader.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.commons.chess import com.vitorpamplona.quartz.nip64Chess.ChessEngine import com.vitorpamplona.quartz.nip64Chess.ChessStateReconstructor import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState import com.vitorpamplona.quartz.nip64Chess.ReconstructedGameState import com.vitorpamplona.quartz.nip64Chess.ReconstructionResult diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt index 56ae30b3d..3a4043f93 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt @@ -23,8 +23,8 @@ package com.vitorpamplona.amethyst.commons.chess import com.vitorpamplona.quartz.nip64Chess.ChessGameEnd import com.vitorpamplona.quartz.nip64Chess.ChessMoveEvent import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents import com.vitorpamplona.quartz.nip64Chess.PieceType import com.vitorpamplona.quartz.utils.TimeUtils import kotlinx.coroutines.CoroutineScope diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/subscription/ChessFilterBuilder.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/subscription/ChessFilterBuilder.kt index 90ff1070e..4057a696e 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/subscription/ChessFilterBuilder.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/subscription/ChessFilterBuilder.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.commons.chess.subscription import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol import com.vitorpamplona.quartz.utils.TimeUtils /** diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventBroadcaster.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventBroadcaster.kt index ac6e7d896..59c37d4df 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventBroadcaster.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventBroadcaster.kt @@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol import kotlinx.coroutines.delay /** diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessAdapter.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessAdapter.kt index f80a3f6a6..d20a2247b 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessAdapter.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessAdapter.kt @@ -37,10 +37,10 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip64Chess.ChessGameEnd import com.vitorpamplona.quartz.nip64Chess.ChessMoveEvent import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.JesterGameEvents -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol -import com.vitorpamplona.quartz.nip64Chess.toJesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent /** * Desktop implementation of ChessEventPublisher using Jester protocol. diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessEventCache.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessEventCache.kt index c779afebd..0e9fd9a02 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessEventCache.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessEventCache.kt @@ -21,10 +21,10 @@ package com.vitorpamplona.amethyst.desktop.chess import com.vitorpamplona.quartz.nip01Core.core.Event -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.JesterGameEvents -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol -import com.vitorpamplona.quartz.nip64Chess.toJesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent import java.util.concurrent.ConcurrentHashMap /** diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt index 895aed70a..5958c3aa5 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt @@ -32,9 +32,9 @@ import com.vitorpamplona.amethyst.desktop.account.AccountState import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip64Chess.Color -import com.vitorpamplona.quartz.nip64Chess.JesterProtocol +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol import com.vitorpamplona.quartz.nip64Chess.LiveChessGameState -import com.vitorpamplona.quartz.nip64Chess.toJesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.toJesterEvent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/subscriptions/ChessSubscription.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/subscriptions/ChessSubscription.kt index 81d9bbde2..fa133d8a2 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/subscriptions/ChessSubscription.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/subscriptions/ChessSubscription.kt @@ -30,11 +30,11 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.groupByRelay import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import com.vitorpamplona.quartz.nip64Chess.LiveChessDrawOfferEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameAcceptEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessMoveEvent +import com.vitorpamplona.quartz.nip64Chess.draw.LiveChessDrawOfferEvent +import com.vitorpamplona.quartz.nip64Chess.accept.LiveChessGameAcceptEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent import com.vitorpamplona.quartz.utils.TimeUtils import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessStateReconstructor.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessStateReconstructor.kt index 65849aa77..cb6542ce9 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessStateReconstructor.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessStateReconstructor.kt @@ -21,6 +21,9 @@ package com.vitorpamplona.quartz.nip64Chess import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterGameEvents +import com.vitorpamplona.quartz.nip64Chess.jester.JesterProtocol /** * Deterministic chess state reconstruction from Jester protocol events. diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/LiveChessGameEvents.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/LiveChessGameEvents.kt deleted file mode 100644 index 4381f832c..000000000 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/LiveChessGameEvents.kt +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.quartz.nip64Chess - -import androidx.compose.runtime.Immutable -import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent -import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder -import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate -import com.vitorpamplona.quartz.nip31Alts.alt -import com.vitorpamplona.quartz.utils.TimeUtils - -/** - * Live Chess Game Challenge Event (Kind 30064) - * - * Challenge another player to a chess game or create an open challenge. - * This is a parameterized replaceable event. - * - * Tags: - * - d: game_id (unique identifier for this game) - * - p: opponent pubkey (optional, for direct challenges) - * - player_color: "white" or "black" - * - time_control: optional time control (e.g., "10+0", "5+3") - */ -@Immutable -class LiveChessGameChallengeEvent( - id: HexKey, - pubKey: HexKey, - createdAt: Long, - tags: Array>, - content: String, - sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - companion object { - const val KIND = 30064 - - fun build( - gameId: String, - playerColor: Color, - opponentPubkey: String? = null, - timeControl: String? = null, - createdAt: Long = TimeUtils.now(), - initializer: TagArrayBuilder.() -> Unit = {}, - ) = eventTemplate(KIND, "", createdAt) { - add(arrayOf("d", gameId)) - add(arrayOf("player_color", if (playerColor == Color.WHITE) "white" else "black")) - opponentPubkey?.let { add(arrayOf("p", it)) } - timeControl?.let { add(arrayOf("time_control", it)) } - alt("Chess game challenge") - initializer() - } - } - - fun gameId(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "d" }?.get(1) - - fun playerColor(): Color? = - tags - .firstOrNull { it.size >= 2 && it[0] == "player_color" } - ?.get(1) - ?.let { if (it == "white") Color.WHITE else Color.BLACK } - - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - - fun timeControl(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "time_control" }?.get(1) -} - -/** - * Live Chess Game Accept Event (Kind 30065) - * - * Accept a chess game challenge - * - * Tags: - * - d: game_id (same as challenge) - * - e: challenge event ID - * - p: challenger pubkey - */ -@Immutable -class LiveChessGameAcceptEvent( - id: HexKey, - pubKey: HexKey, - createdAt: Long, - tags: Array>, - content: String, - sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - companion object { - const val KIND = 30065 - - fun build( - gameId: String, - challengeEventId: String, - challengerPubkey: String, - createdAt: Long = TimeUtils.now(), - initializer: TagArrayBuilder.() -> Unit = {}, - ) = eventTemplate(KIND, "", createdAt) { - add(arrayOf("d", gameId)) - add(arrayOf("e", challengeEventId)) - add(arrayOf("p", challengerPubkey)) - alt("Chess game acceptance") - initializer() - } - } - - fun gameId(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "d" }?.get(1) - - fun challengeEventId(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "e" }?.get(1) - - fun challengerPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) -} - -/** - * Live Chess Move Event (Kind 30066) - * - * Individual move in a live chess game - * - * Tags: - * - d: game_id - * - move_number: move number (1-based) - * - san: move in Standard Algebraic Notation - * - fen: resulting position in FEN notation - * - p: opponent pubkey - * - * Content: Optional move comment - */ -@Immutable -class LiveChessMoveEvent( - id: HexKey, - pubKey: HexKey, - createdAt: Long, - tags: Array>, - content: String, - sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - companion object { - const val KIND = 30066 - - fun build( - gameId: String, - moveNumber: Int, - san: String, - fen: String, - opponentPubkey: String, - comment: String = "", - createdAt: Long = TimeUtils.now(), - initializer: TagArrayBuilder.() -> Unit = {}, - ) = eventTemplate(KIND, comment, createdAt) { - add(arrayOf("d", "$gameId-$moveNumber")) - add(arrayOf("game_id", gameId)) - add(arrayOf("move_number", moveNumber.toString())) - add(arrayOf("san", san)) - add(arrayOf("fen", fen)) - add(arrayOf("p", opponentPubkey)) - alt("Chess move: $san") - initializer() - } - } - - fun gameId(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "game_id" }?.get(1) - - fun moveNumber(): Int? = - tags - .firstOrNull { it.size >= 2 && it[0] == "move_number" } - ?.get(1) - ?.toIntOrNull() - - fun san(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "san" }?.get(1) - - fun fen(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "fen" }?.get(1) - - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - - fun comment(): String = content -} - -/** - * Live Chess Game End Event (Kind 30067) - * - * Game result and termination - * - * Tags: - * - d: game_id - * - result: "1-0"|"0-1"|"1/2-1/2" - * - termination: reason for game end - * - winner: pubkey of winner (if applicable) - * - p: opponent pubkey - * - * Content: Optional PGN of complete game - */ -@Immutable -class LiveChessGameEndEvent( - id: HexKey, - pubKey: HexKey, - createdAt: Long, - tags: Array>, - content: String, - sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - companion object { - const val KIND = 30067 - - fun build( - gameId: String, - result: GameResult, - termination: GameTermination, - winnerPubkey: String? = null, - opponentPubkey: String, - pgn: String = "", - createdAt: Long = TimeUtils.now(), - initializer: TagArrayBuilder.() -> Unit = {}, - ) = eventTemplate(KIND, pgn, createdAt) { - add(arrayOf("d", gameId)) - add(arrayOf("result", result.notation)) - add(arrayOf("termination", termination.name.lowercase())) - winnerPubkey?.let { add(arrayOf("winner", it)) } - add(arrayOf("p", opponentPubkey)) - alt("Chess game ended: ${result.notation}") - initializer() - } - } - - fun gameId(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "d" }?.get(1) - - fun result(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "result" }?.get(1) - - fun termination(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "termination" }?.get(1) - - fun winnerPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "winner" }?.get(1) - - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - - fun pgn(): String = content -} - -/** - * Live Chess Draw Offer Event (Kind 30068) - * - * Offer a draw to opponent. Opponent can accept by sending a game end event - * with DRAW_AGREEMENT termination, or decline/ignore by making their next move. - * - * Tags: - * - d: game_id - * - p: opponent pubkey - * - * Content: Optional message - */ -@Immutable -class LiveChessDrawOfferEvent( - id: HexKey, - pubKey: HexKey, - createdAt: Long, - tags: Array>, - content: String, - sig: HexKey, -) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { - companion object { - const val KIND = 30068 - - fun build( - gameId: String, - opponentPubkey: String, - message: String = "", - createdAt: Long = TimeUtils.now(), - initializer: TagArrayBuilder.() -> Unit = {}, - ) = eventTemplate(KIND, message, createdAt) { - add(arrayOf("d", gameId)) - add(arrayOf("p", opponentPubkey)) - alt("Chess draw offer") - initializer() - } - } - - fun gameId(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "d" }?.get(1) - - fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) - - fun message(): String = content -} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt new file mode 100644 index 000000000..16872f468 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/LiveChessGameAcceptEvent.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.accept + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.utils.TimeUtils + +/** + * Live Chess Game Accept Event (Kind 30065) + * + * Accept a chess game challenge + * + * Tags: + * - d: game_id (same as challenge) + * - e: challenge event ID + * - p: challenger pubkey + */ +@Immutable +class LiveChessGameAcceptEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun challengeEventId() = tags.challengeEventId() + + fun challengerPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) + + companion object { + const val KIND = 30065 + const val ALT_DESCRIPTION = "Chess game acceptance" + + fun build( + gameId: String, + challengeEventId: String, + challengerPubkey: String, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + add(arrayOf("d", gameId)) + challengeEvent(challengeEventId) + add(arrayOf("p", challengerPubkey)) + alt(ALT_DESCRIPTION) + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt new file mode 100644 index 000000000..4f0e0391c --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayBuilderExt.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.accept + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip64Chess.accept.tags.ChallengeEventTag + +fun TagArrayBuilder.challengeEvent(challengeEventId: String) = add(ChallengeEventTag.assemble(challengeEventId)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayExt.kt new file mode 100644 index 000000000..86a529305 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/TagArrayExt.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.accept + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip64Chess.accept.tags.ChallengeEventTag + +fun TagArray.challengeEventId() = firstNotNullOfOrNull(ChallengeEventTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt new file mode 100644 index 000000000..803b0e869 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/accept/tags/ChallengeEventTag.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.accept.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class ChallengeEventTag { + companion object { + const val TAG_NAME = "e" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64 + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].length == 64) { return null } + return tag[1] + } + + fun assemble(challengeEventId: String) = arrayOf(TAG_NAME, challengeEventId) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt new file mode 100644 index 000000000..98cc21fa7 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/LiveChessGameChallengeEvent.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.challenge + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip64Chess.Color +import com.vitorpamplona.quartz.utils.TimeUtils + +/** + * Live Chess Game Challenge Event (Kind 30064) + * + * Challenge another player to a chess game or create an open challenge. + * This is a parameterized replaceable event. + * + * Tags: + * - d: game_id (unique identifier for this game) + * - p: opponent pubkey (optional, for direct challenges) + * - player_color: "white" or "black" + * - time_control: optional time control (e.g., "10+0", "5+3") + */ +@Immutable +class LiveChessGameChallengeEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun playerColor() = tags.playerColor() + + fun timeControl() = tags.timeControl() + + fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) + + companion object { + const val KIND = 30064 + const val ALT_DESCRIPTION = "Chess game challenge" + + fun build( + gameId: String, + playerColor: Color, + opponentPubkey: String? = null, + timeControl: String? = null, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, "", createdAt) { + add(arrayOf("d", gameId)) + playerColor(playerColor) + opponentPubkey?.let { add(arrayOf("p", it)) } + timeControl?.let { timeControl(it) } + alt(ALT_DESCRIPTION) + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/TagArrayBuilderExt.kt new file mode 100644 index 000000000..a110d2405 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/TagArrayBuilderExt.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.challenge + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip64Chess.Color +import com.vitorpamplona.quartz.nip64Chess.challenge.tags.PlayerColorTag +import com.vitorpamplona.quartz.nip64Chess.challenge.tags.TimeControlTag + +fun TagArrayBuilder.playerColor(color: Color) = addUnique(PlayerColorTag.assemble(color)) + +fun TagArrayBuilder.timeControl(timeControl: String) = addUnique(TimeControlTag.assemble(timeControl)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/TagArrayExt.kt new file mode 100644 index 000000000..01a247b87 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/TagArrayExt.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.challenge + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip64Chess.challenge.tags.PlayerColorTag +import com.vitorpamplona.quartz.nip64Chess.challenge.tags.TimeControlTag + +fun TagArray.playerColor() = firstNotNullOfOrNull(PlayerColorTag::parse) + +fun TagArray.timeControl() = firstNotNullOfOrNull(TimeControlTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/tags/PlayerColorTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/tags/PlayerColorTag.kt new file mode 100644 index 000000000..983ab756d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/tags/PlayerColorTag.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.challenge.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.nip64Chess.Color +import com.vitorpamplona.quartz.utils.ensure + +class PlayerColorTag { + companion object { + const val TAG_NAME = "player_color" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): Color? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return when (tag[1]) { + "white" -> Color.WHITE + "black" -> Color.BLACK + else -> null + } + } + + fun assemble(color: Color) = arrayOf(TAG_NAME, if (color == Color.WHITE) "white" else "black") + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/tags/TimeControlTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/tags/TimeControlTag.kt new file mode 100644 index 000000000..84f1c9494 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/challenge/tags/TimeControlTag.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.challenge.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class TimeControlTag { + companion object { + const val TAG_NAME = "time_control" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(timeControl: String) = arrayOf(TAG_NAME, timeControl) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt new file mode 100644 index 000000000..ab7c05076 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/draw/LiveChessDrawOfferEvent.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.draw + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.utils.TimeUtils + +/** + * Live Chess Draw Offer Event (Kind 30068) + * + * Offer a draw to opponent. Opponent can accept by sending a game end event + * with DRAW_AGREEMENT termination, or decline/ignore by making their next move. + * + * Tags: + * - d: game_id + * - p: opponent pubkey + * + * Content: Optional message + */ +@Immutable +class LiveChessDrawOfferEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) + + fun message(): String = content + + companion object { + const val KIND = 30068 + const val ALT_DESCRIPTION = "Chess draw offer" + + fun build( + gameId: String, + opponentPubkey: String, + message: String = "", + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, message, createdAt) { + add(arrayOf("d", gameId)) + add(arrayOf("p", opponentPubkey)) + alt(ALT_DESCRIPTION) + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt new file mode 100644 index 000000000..f7f2773ec --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/LiveChessGameEndEvent.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.end + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip64Chess.GameResult +import com.vitorpamplona.quartz.nip64Chess.GameTermination +import com.vitorpamplona.quartz.utils.TimeUtils + +/** + * Live Chess Game End Event (Kind 30067) + * + * Game result and termination + * + * Tags: + * - d: game_id + * - result: "1-0"|"0-1"|"1/2-1/2" + * - termination: reason for game end + * - winner: pubkey of winner (if applicable) + * - p: opponent pubkey + * + * Content: Optional PGN of complete game + */ +@Immutable +class LiveChessGameEndEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun result() = tags.result() + + fun termination() = tags.termination() + + fun winnerPubkey() = tags.winnerPubkey() + + fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) + + fun pgn(): String = content + + companion object { + const val KIND = 30067 + const val ALT_DESCRIPTION = "Chess game ended" + + fun build( + gameId: String, + result: GameResult, + termination: GameTermination, + winnerPubkey: String? = null, + opponentPubkey: String, + pgn: String = "", + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, pgn, createdAt) { + add(arrayOf("d", gameId)) + result(result) + termination(termination) + winnerPubkey?.let { winner(it) } + add(arrayOf("p", opponentPubkey)) + alt("$ALT_DESCRIPTION: ${result.notation}") + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/TagArrayBuilderExt.kt new file mode 100644 index 000000000..22da9eae7 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/TagArrayBuilderExt.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.end + +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip64Chess.GameResult +import com.vitorpamplona.quartz.nip64Chess.GameTermination +import com.vitorpamplona.quartz.nip64Chess.end.tags.ResultTag +import com.vitorpamplona.quartz.nip64Chess.end.tags.TerminationTag +import com.vitorpamplona.quartz.nip64Chess.end.tags.WinnerTag + +fun TagArrayBuilder.result(result: GameResult) = addUnique(ResultTag.assemble(result)) + +fun TagArrayBuilder.termination(termination: GameTermination) = addUnique(TerminationTag.assemble(termination)) + +fun TagArrayBuilder.winner(winnerPubkey: HexKey) = addUnique(WinnerTag.assemble(winnerPubkey)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/TagArrayExt.kt new file mode 100644 index 000000000..55ea46743 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/TagArrayExt.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.end + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip64Chess.end.tags.ResultTag +import com.vitorpamplona.quartz.nip64Chess.end.tags.TerminationTag +import com.vitorpamplona.quartz.nip64Chess.end.tags.WinnerTag + +fun TagArray.result() = firstNotNullOfOrNull(ResultTag::parse) + +fun TagArray.termination() = firstNotNullOfOrNull(TerminationTag::parse) + +fun TagArray.winnerPubkey() = firstNotNullOfOrNull(WinnerTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/ResultTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/ResultTag.kt new file mode 100644 index 000000000..769246b08 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/ResultTag.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.end.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.nip64Chess.GameResult +import com.vitorpamplona.quartz.utils.ensure + +class ResultTag { + companion object { + const val TAG_NAME = "result" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(result: GameResult) = arrayOf(TAG_NAME, result.notation) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/TerminationTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/TerminationTag.kt new file mode 100644 index 000000000..9a22a5292 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/TerminationTag.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.end.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.nip64Chess.GameTermination +import com.vitorpamplona.quartz.utils.ensure + +class TerminationTag { + companion object { + const val TAG_NAME = "termination" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(termination: GameTermination) = arrayOf(TAG_NAME, termination.name.lowercase()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/WinnerTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/WinnerTag.kt new file mode 100644 index 000000000..c260a465b --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/end/tags/WinnerTag.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.end.tags + +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class WinnerTag { + companion object { + const val TAG_NAME = "winner" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): HexKey? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(winnerPubkey: HexKey) = arrayOf(TAG_NAME, winnerPubkey) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessGameEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/game/ChessGameEvent.kt similarity index 98% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessGameEvent.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/game/ChessGameEvent.kt index 664c60b0b..f0d7f00e7 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/ChessGameEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/game/ChessGameEvent.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.nip64Chess +package com.vitorpamplona.quartz.nip64Chess.game import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event @@ -50,6 +50,16 @@ class ChessGameEvent( content: String, // PGN database format sig: HexKey, ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { + /** + * Get PGN content from event + */ + fun pgn(): String = content + + /** + * Get alt text for non-supporting clients (NIP-31) + */ + fun altText(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "alt" }?.get(1) + companion object { const val KIND = 64 const val ALT_DESCRIPTION = "Chess Game" @@ -73,14 +83,4 @@ class ChessGameEvent( initializer() } } - - /** - * Get PGN content from event - */ - fun pgn(): String = content - - /** - * Get alt text for non-supporting clients (NIP-31) - */ - fun altText(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "alt" }?.get(1) } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterContent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterContent.kt new file mode 100644 index 000000000..0eb536cd3 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterContent.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.jester + +import kotlinx.serialization.Serializable + +/** + * JSON content structure for Jester events + */ +@Serializable +data class JesterContent( + val version: String = "0", + val kind: Int, + val fen: String = JesterProtocol.FEN_START, + val move: String? = null, + val history: List = emptyList(), + val nonce: String? = null, + // Extended fields for Amethyst (backward compatible - jesterui ignores unknown fields) + val playerColor: String? = null, // "white" or "black" - challenger's color choice + val result: String? = null, // "1-0", "0-1", "1/2-1/2" for game end + val termination: String? = null, // "checkmate", "resignation", "draw_agreement", etc. +) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/JesterEvents.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterEvent.kt similarity index 80% rename from quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/JesterEvents.kt rename to quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterEvent.kt index f650b2194..6b9075837 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/JesterEvents.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterEvent.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.nip64Chess +package com.vitorpamplona.quartz.nip64Chess.jester import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.nip01Core.core.Event @@ -26,65 +26,12 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip64Chess.Color +import com.vitorpamplona.quartz.nip64Chess.GameResult +import com.vitorpamplona.quartz.nip64Chess.GameTermination import com.vitorpamplona.quartz.utils.TimeUtils -import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json -/** - * Jester Protocol Implementation - * - * Compatible with jesterui (https://github.com/jesterui/jesterui) - * - * Key differences from previous implementation: - * - Single event kind (30) for all chess messages - * - Content is JSON with: version, kind, fen, move, history, nonce - * - Event linking via e-tags: [startId] or [startId, headId] - * - Full move history included in every move event - * - * Content kind values: - * - 0: Game start (challenge) - * - 1: Move - * - 2: Chat (not implemented) - * - * Reference: https://github.com/jesterui/jesterui/blob/devel/FLOW.md - */ -object JesterProtocol { - /** Jester uses kind 30 for all chess events */ - const val KIND = 30 - - /** SHA256 of starting FEN position - used as reference for game discovery */ - const val START_POSITION_HASH = "b1791d7fc9ae3d38966568c257ffb3a02cbf8394cdb4805bc70f64fc3c0b6879" - - /** Standard starting FEN */ - const val FEN_START = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" - - /** Content kind for game start */ - const val CONTENT_KIND_START = 0 - - /** Content kind for move */ - const val CONTENT_KIND_MOVE = 1 - - /** Content kind for chat */ - const val CONTENT_KIND_CHAT = 2 -} - -/** - * JSON content structure for Jester events - */ -@Serializable -data class JesterContent( - val version: String = "0", - val kind: Int, - val fen: String = JesterProtocol.FEN_START, - val move: String? = null, - val history: List = emptyList(), - val nonce: String? = null, - // Extended fields for Amethyst (backward compatible - jesterui ignores unknown fields) - val playerColor: String? = null, // "white" or "black" - challenger's color choice - val result: String? = null, // "1-0", "0-1", "1/2-1/2" for game end - val termination: String? = null, // "checkmate", "resignation", "draw_agreement", etc. -) - private val json = Json { ignoreUnknownKeys = true @@ -352,30 +299,3 @@ fun Event.toJesterEvent(): JesterEvent? { if (kind != JesterProtocol.KIND) return null return JesterEvent(id, pubKey, createdAt, tags, content, sig) } - -/** - * Game events container for Jester protocol - */ -data class JesterGameEvents( - val startEvent: JesterEvent?, - val moves: List, -) { - companion object { - fun empty() = JesterGameEvents(null, emptyList()) - } - - /** Get the latest move (with longest history) */ - fun latestMove(): JesterEvent? = moves.maxByOrNull { it.history().size } - - /** Get the current FEN position */ - fun currentFen(): String = latestMove()?.fen() ?: startEvent?.fen() ?: JesterProtocol.FEN_START - - /** Get the complete move history */ - fun fullHistory(): List = latestMove()?.history() ?: emptyList() - - /** Check if game has ended */ - fun isEnded(): Boolean = latestMove()?.result() != null - - /** Get the game result if ended */ - fun result(): String? = latestMove()?.result() -} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterGameEvents.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterGameEvents.kt new file mode 100644 index 000000000..039805742 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterGameEvents.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.jester + +/** + * Game events container for Jester protocol + */ +data class JesterGameEvents( + val startEvent: JesterEvent?, + val moves: List, +) { + companion object { + fun empty() = JesterGameEvents(null, emptyList()) + } + + /** Get the latest move (with longest history) */ + fun latestMove(): JesterEvent? = moves.maxByOrNull { it.history().size } + + /** Get the current FEN position */ + fun currentFen(): String = latestMove()?.fen() ?: startEvent?.fen() ?: JesterProtocol.FEN_START + + /** Get the complete move history */ + fun fullHistory(): List = latestMove()?.history() ?: emptyList() + + /** Check if game has ended */ + fun isEnded(): Boolean = latestMove()?.result() != null + + /** Get the game result if ended */ + fun result(): String? = latestMove()?.result() +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterProtocol.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterProtocol.kt new file mode 100644 index 000000000..0ecf6df8d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/jester/JesterProtocol.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.jester + +/** + * Jester Protocol Constants + * + * Compatible with jesterui (https://github.com/jesterui/jesterui) + * + * Key differences from previous implementation: + * - Single event kind (30) for all chess messages + * - Content is JSON with: version, kind, fen, move, history, nonce + * - Event linking via e-tags: [startId] or [startId, headId] + * - Full move history included in every move event + * + * Content kind values: + * - 0: Game start (challenge) + * - 1: Move + * - 2: Chat (not implemented) + * + * Reference: https://github.com/jesterui/jesterui/blob/devel/FLOW.md + */ +object JesterProtocol { + /** Jester uses kind 30 for all chess events */ + const val KIND = 30 + + /** SHA256 of starting FEN position - used as reference for game discovery */ + const val START_POSITION_HASH = "b1791d7fc9ae3d38966568c257ffb3a02cbf8394cdb4805bc70f64fc3c0b6879" + + /** Standard starting FEN */ + const val FEN_START = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" + + /** Content kind for game start */ + const val CONTENT_KIND_START = 0 + + /** Content kind for move */ + const val CONTENT_KIND_MOVE = 1 + + /** Content kind for chat */ + const val CONTENT_KIND_CHAT = 2 +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt new file mode 100644 index 000000000..869930687 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/LiveChessMoveEvent.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move + +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.utils.TimeUtils + +/** + * Live Chess Move Event (Kind 30066) + * + * Individual move in a live chess game + * + * Tags: + * - d: game_id + * - move_number: move number (1-based) + * - san: move in Standard Algebraic Notation + * - fen: resulting position in FEN notation + * - p: opponent pubkey + * + * Content: Optional move comment + */ +@Immutable +class LiveChessMoveEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: Array>, + content: String, + sig: HexKey, +) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + fun gameId() = tags.gameId() + + fun moveNumber() = tags.moveNumber() + + fun san() = tags.san() + + fun fen() = tags.fen() + + fun opponentPubkey(): String? = tags.firstOrNull { it.size >= 2 && it[0] == "p" }?.get(1) + + fun comment(): String = content + + companion object { + const val KIND = 30066 + const val ALT_DESCRIPTION = "Chess move" + + fun build( + gameId: String, + moveNumber: Int, + san: String, + fen: String, + opponentPubkey: String, + comment: String = "", + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, comment, createdAt) { + add(arrayOf("d", "$gameId-$moveNumber")) + gameId(gameId) + moveNumber(moveNumber) + san(san) + fen(fen) + add(arrayOf("p", opponentPubkey)) + alt("$ALT_DESCRIPTION: $san") + initializer() + } + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/TagArrayBuilderExt.kt new file mode 100644 index 000000000..0a6a0d33c --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/TagArrayBuilderExt.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move + +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder +import com.vitorpamplona.quartz.nip64Chess.move.tags.FenTag +import com.vitorpamplona.quartz.nip64Chess.move.tags.GameIdTag +import com.vitorpamplona.quartz.nip64Chess.move.tags.MoveNumberTag +import com.vitorpamplona.quartz.nip64Chess.move.tags.SanTag + +fun TagArrayBuilder.gameId(gameId: String) = addUnique(GameIdTag.assemble(gameId)) + +fun TagArrayBuilder.moveNumber(moveNumber: Int) = addUnique(MoveNumberTag.assemble(moveNumber)) + +fun TagArrayBuilder.san(san: String) = addUnique(SanTag.assemble(san)) + +fun TagArrayBuilder.fen(fen: String) = addUnique(FenTag.assemble(fen)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/TagArrayExt.kt new file mode 100644 index 000000000..9651d9a85 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/TagArrayExt.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move + +import com.vitorpamplona.quartz.nip01Core.core.TagArray +import com.vitorpamplona.quartz.nip64Chess.move.tags.FenTag +import com.vitorpamplona.quartz.nip64Chess.move.tags.GameIdTag +import com.vitorpamplona.quartz.nip64Chess.move.tags.MoveNumberTag +import com.vitorpamplona.quartz.nip64Chess.move.tags.SanTag + +fun TagArray.gameId() = firstNotNullOfOrNull(GameIdTag::parse) + +fun TagArray.moveNumber() = firstNotNullOfOrNull(MoveNumberTag::parse) + +fun TagArray.san() = firstNotNullOfOrNull(SanTag::parse) + +fun TagArray.fen() = firstNotNullOfOrNull(FenTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/FenTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/FenTag.kt new file mode 100644 index 000000000..bda7f867e --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/FenTag.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class FenTag { + companion object { + const val TAG_NAME = "fen" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(fen: String) = arrayOf(TAG_NAME, fen) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/GameIdTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/GameIdTag.kt new file mode 100644 index 000000000..4f16bb095 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/GameIdTag.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class GameIdTag { + companion object { + const val TAG_NAME = "game_id" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(gameId: String) = arrayOf(TAG_NAME, gameId) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/MoveNumberTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/MoveNumberTag.kt new file mode 100644 index 000000000..6978c5b3d --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/MoveNumberTag.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class MoveNumberTag { + companion object { + const val TAG_NAME = "move_number" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): Int? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1].toIntOrNull() + } + + fun assemble(moveNumber: Int) = arrayOf(TAG_NAME, moveNumber.toString()) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/SanTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/SanTag.kt new file mode 100644 index 000000000..6d3897676 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip64Chess/move/tags/SanTag.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip64Chess.move.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class SanTag { + companion object { + const val TAG_NAME = "san" + + fun isTag(tag: Array) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty() + + fun parse(tag: Array): String? { + ensure(tag.has(1) && tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return tag[1] + } + + fun assemble(san: String) = arrayOf(TAG_NAME, san) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt index 9cb287dc6..a3ace34f8 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt @@ -114,13 +114,13 @@ import com.vitorpamplona.quartz.nip58Badges.BadgeProfilesEvent import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent -import com.vitorpamplona.quartz.nip64Chess.ChessGameEvent -import com.vitorpamplona.quartz.nip64Chess.JesterEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessDrawOfferEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameAcceptEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameChallengeEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessGameEndEvent -import com.vitorpamplona.quartz.nip64Chess.LiveChessMoveEvent +import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent +import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent +import com.vitorpamplona.quartz.nip64Chess.draw.LiveChessDrawOfferEvent +import com.vitorpamplona.quartz.nip64Chess.accept.LiveChessGameAcceptEvent +import com.vitorpamplona.quartz.nip64Chess.challenge.LiveChessGameChallengeEvent +import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent +import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip66RelayMonitor.discovery.RelayDiscoveryEvent import com.vitorpamplona.quartz.nip66RelayMonitor.monitor.RelayMonitorEvent