import SwiftUI struct GameBoardView: View { @Bindable var viewModel: GameViewModel let theme: GameTheme let cardFaceStyle: CardFaceStyle let cardBackDesign: CardBackDesign private var isReady: Bool { viewModel.state.phase != .notStarted && !viewModel.state.tableaus.isEmpty } private var deepestColumn: (faceDown: Int, faceUp: Int) { var worst = (faceDown: 0, faceUp: 1) for column in viewModel.state.tableaus { let faceUp = column.reversed().prefix(while: { $0.isFaceUp }).count let faceDown = column.count - faceUp let depth = faceDown * 15 + max(0, faceUp - 1) * 25 + 100 let worstDepth = worst.faceDown * 15 + max(0, worst.faceUp - 1) * 25 + 100 if depth > worstDepth { worst = (faceDown, faceUp) } } return worst } var body: some View { if isReady { VStack(spacing: 0) { ScoreBarView( moves: viewModel.state.moves, score: viewModel.state.score, time: viewModel.elapsedSeconds.formattedTime, theme: theme ) GeometryReader { geometry in let layout = CardLayout( availableSize: geometry.size, variant: viewModel.variant, deepestColumn: deepestColumn ) ScrollView(.vertical) { boardContent(layout: layout) .padding(.bottom, layout.horizontalPadding) .frame(maxWidth: .infinity, minHeight: landscapeMinHeight(viewportHeight: geometry.size.height, isLandscape: layout.isLandscape), alignment: .top) .contentShape(Rectangle()) } .scrollIndicators(.hidden) .scrollDisabled(!viewModel.draggedCards.isEmpty) .coordinateSpace(name: "board") .onPreferenceChange(DropTargetPreferenceKey.self) { targets in viewModel.dropTargets = targets } .overlay { DraggedCardsOverlay( viewModel: viewModel, layout: layout, cardFaceStyle: cardFaceStyle, cardBackDesign: cardBackDesign ) } } } .background(theme.backgroundColor) .overlay { if viewModel.isWon { VictoryOverlayView { viewModel.newGame() } } } } else { Color.clear .background(theme.backgroundColor) } } private func landscapeMinHeight(viewportHeight: CGFloat, isLandscape: Bool) -> CGFloat? { #if os(iOS) return isLandscape ? viewportHeight * 1.3 : nil #else return nil #endif } @ViewBuilder private func boardContent(layout: CardLayout) -> some View { switch viewModel.variant { case .klondike: KlondikeBoardView(viewModel: viewModel, layout: layout, theme: theme, cardFaceStyle: cardFaceStyle, cardBackDesign: cardBackDesign) case .spider: SpiderBoardView(viewModel: viewModel, layout: layout, theme: theme, cardFaceStyle: cardFaceStyle, cardBackDesign: cardBackDesign) case .freeCell: FreeCellBoardView(viewModel: viewModel, layout: layout, theme: theme, cardFaceStyle: cardFaceStyle, cardBackDesign: cardBackDesign) } } } extension Int { var formattedTime: String { let minutes = self / 60 let seconds = self % 60 return String(format: "%d:%02d", minutes, seconds) } }