Pertemuan 10 - APLIKASI WORD SCRAMBLE
Nama : Armadya Hermawan
NRP : 5025211243
Kelas : PPB A
@Composable
fun WordGuessingGameScreen(viewModel: WordGameViewModel = viewModel()) {
val uiState by viewModel.gameState.collectAsState()
val largePadding = dimensionResource(R.dimen.padding_large)
val smallPadding = dimensionResource(R.dimen.padding_small)
Box(
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.safeDrawingPadding()
.padding(largePadding)
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
item {
GameHeaderSection(
appTitle = stringResource(R.string.app_name),
currentRound = uiState.roundNumber,
totalRounds = uiState.totalRounds
)
}
item {
WordDisplayCard(
scrambledWord = uiState.scrambledWord,
wordHint = uiState.wordHint,
isAnimating = uiState.isWordAnimating,
modifier = Modifier.padding(vertical = smallPadding)
)
}
item {
UserInputSection(
playerAnswer = uiState.playerInput,
onAnswerChange = { viewModel.updatePlayerInput(it) },
onSubmitAnswer = { viewModel.validateAnswer() },
hasError = uiState.hasInputError,
errorMessage = uiState.errorMessage,
isEnabled = !uiState.isProcessing
)
}
item {
ActionButtonsRow(
onSubmit = { viewModel.validateAnswer() },
onSkip = { viewModel.skipCurrentWord() },
onHint = { viewModel.showHint() },
isProcessing = uiState.isProcessing,
canShowHint = uiState.hintsRemaining > 0
)
}
item {
ScoreboardCard(
currentScore = uiState.playerScore,
hintsUsed = uiState.hintsUsed,
wordsCompleted = uiState.wordsCompleted,
modifier = Modifier.padding(top = largePadding)
)
}
}
if (uiState.showGameOverDialog) {
GameCompletionDialog(
finalScore = uiState.playerScore,
totalWords = uiState.wordsCompleted,
onRestart = { viewModel.restartGame() },
onExit = { viewModel.exitGame() }
)
}
}
}
@Composable
fun GameHeaderSection(
appTitle: String,
currentRound: Int,
totalRounds: Int,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = appTitle,
style = typography.headlineLarge,
color = colorScheme.primary,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(8.dp))
LinearProgressIndicator(
progress = currentRound.toFloat() / totalRounds.toFloat(),
modifier = Modifier
.fillMaxWidth()
.height(4.dp)
.clip(RoundedCornerShape(2.dp)),
color = colorScheme.secondary
)
Text(
text = "Round $currentRound of $totalRounds",
style = typography.labelMedium,
color = colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
}
}
@Composable
fun WordDisplayCard(
scrambledWord: String,
wordHint: String?,
isAnimating: Boolean,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = colorScheme.primaryContainer
),
elevation = CardDefaults.cardElevation(
defaultElevation = 8.dp
)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Guess this word:",
style = typography.titleSmall,
color = colorScheme.onPrimaryContainer
)
Spacer(modifier = Modifier.height(16.dp))
AnimatedContent(
targetState = scrambledWord,
transitionSpec = {
if (isAnimating) {
slideInHorizontally { it } with slideOutHorizontally { -it }
} else {
fadeIn() with fadeOut()
}
}
) { word ->
Text(
text = word,
style = typography.displaySmall,
color = colorScheme.onPrimaryContainer,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}
if (wordHint != null) {
Spacer(modifier = Modifier.height(16.dp))
Surface(
color = colorScheme.secondary,
shape = RoundedCornerShape(8.dp)
) {
Text(
text = "Hint: $wordHint",
style = typography.bodySmall,
color = colorScheme.onSecondary,
modifier = Modifier.padding(8.dp)
)
}
}
}
}
}
@Composable
fun UserInputSection(
playerAnswer: String,
onAnswerChange: (String) -> Unit,
onSubmitAnswer: () -> Unit,
hasError: Boolean,
errorMessage: String?,
isEnabled: Boolean,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.fillMaxWidth()
) {
OutlinedTextField(
value = playerAnswer,
onValueChange = onAnswerChange,
modifier = Modifier.fillMaxWidth(),
placeholder = {
Text(
text = stringResource(R.string.type_your_answer),
style = typography.bodyMedium
)
},
singleLine = true,
enabled = isEnabled,
isError = hasError,
shape = RoundedCornerShape(12.dp),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = colorScheme.primary,
unfocusedBorderColor = colorScheme.outline,
errorBorderColor = colorScheme.error
),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.None,
imeAction = ImeAction.Go
),
keyboardActions = KeyboardActions(
onGo = { onSubmitAnswer() }
)
)
if (hasError && errorMessage != null) {
Text(
text = errorMessage,
color = colorScheme.error,
style = typography.bodySmall,
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
)
}
}
}
@Composable
fun ActionButtonsRow(
onSubmit: () -> Unit,
onSkip: () -> Unit,
onHint: () -> Unit,
isProcessing: Boolean,
canShowHint: Boolean,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Button(
onClick = onSubmit,
enabled = !isProcessing,
modifier = Modifier.weight(1f),
colors = ButtonDefaults.buttonColors(
containerColor = colorScheme.primary
)
) {
if (isProcessing) {
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
color = colorScheme.onPrimary,
strokeWidth = 2.dp
)
} else {
Text(
text = stringResource(R.string.check_answer),
style = typography.labelLarge
)
}
}
OutlinedButton(
onClick = onSkip,
enabled = !isProcessing,
modifier = Modifier.weight(1f)
) {
Text(
text = stringResource(R.string.skip_word),
style = typography.labelLarge
)
}
if (canShowHint) {
IconButton(
onClick = onHint,
enabled = !isProcessing
) {
Icon(
imageVector = Icons.Default.Lightbulb,
contentDescription = "Show hint",
tint = colorScheme.secondary
)
}
}
}
}
@Composable
fun ScoreboardCard(
currentScore: Int,
hintsUsed: Int,
wordsCompleted: Int,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = colorScheme.secondaryContainer
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
StatItem(
label = "Score",
value = currentScore.toString(),
icon = Icons.Default.Star
)
StatItem(
label = "Words",
value = wordsCompleted.toString(),
icon = Icons.Default.CheckCircle
)
StatItem(
label = "Hints",
value = hintsUsed.toString(),
icon = Icons.Default.Lightbulb
)
}
}
}
@Composable
fun StatItem(
label: String,
value: String,
icon: ImageVector,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = icon,
contentDescription = label,
tint = colorScheme.onSecondaryContainer,
modifier = Modifier.size(24.dp)
)
Text(
text = value,
style = typography.headlineSmall,
color = colorScheme.onSecondaryContainer,
fontWeight = FontWeight.Bold
)
Text(
text = label,
style = typography.labelSmall,
color = colorScheme.onSecondaryContainer
)
}
}
@Composable
fun GameCompletionDialog(
finalScore: Int,
totalWords: Int,
onRestart: () -> Unit,
onExit: () -> Unit,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
AlertDialog(
onDismissRequest = { /* Prevent dismissing by clicking outside */ },
modifier = modifier,
title = {
Text(
text = stringResource(R.string.game_completed),
style = typography.headlineSmall,
textAlign = TextAlign.Center
)
},
text = {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.final_score_text, finalScore),
style = typography.bodyLarge,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.words_completed_text, totalWords),
style = typography.bodyMedium,
color = colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center
)
}
},
confirmButton = {
Button(
onClick = onRestart,
colors = ButtonDefaults.buttonColors(
containerColor = colorScheme.primary
)
) {
Text(stringResource(R.string.play_again))
}
},
dismissButton = {
TextButton(
onClick = {
onExit()
if (context is Activity) {
context.finish()
}
}
) {
Text(stringResource(R.string.exit_game))
}
},
shape = RoundedCornerShape(16.dp)
)
}
@Preview(showBackground = true)
@Composable
fun WordGuessingGamePreview() {
UnscrambleTheme {
WordGuessingGameScreen()
}
}
@Preview(showBackground = true)
@Composable
fun GameHeaderPreview() {
UnscrambleTheme {
GameHeaderSection(
appTitle = "Word Scramble",
currentRound = 3,
totalRounds = 10
)
}
}
Comments
Post a Comment