Skip to content

Commit 4f73b55

Browse files
committed
buy flower, make delivery
1 parent bc46b21 commit 4f73b55

8 files changed

Lines changed: 197 additions & 11 deletions

File tree

src/components/round/SideBar.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<template>
22
<div class="sidebar">
33
<div>
4-
{{t('sideBar.round', {round})}}: <span class="fw-bold">{{t(`season.${season}`)}}</span><br/>
5-
{{t('sideBar.turn', {turn})}}
4+
<span class="fw-bold">{{t(`season.${season}`)}}</span> {{t('sideBar.round', {round})}}
65

76
<hr/>
87

src/locales/en.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
}
3434
},
3535
"roundTurnPlayer": {
36+
"selectAction": "Select your action:",
37+
"makeDelivery": "Make Delivery",
38+
"deliveredToFloor": "Delivered to Floor ${floor}",
39+
"otherAction": "Other Action",
40+
"passConfirm": "Do you really want to pass?",
41+
"buyFlower": {
42+
"title": "Buy Flower",
43+
"selectFlower": "Select Flower:",
44+
"numberOfFlowers": "Number of Flowers:",
45+
"totalCost": "Total Cost:"
46+
},
3647
"passInfo": {
3748
"nextTurn": "Pass next turn",
3849
"billboard": "M{floor}"

src/util/NavigationState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function getPlayerDeliveryFloor(round: number, turn: number, state: State) : num
5555
}
5656

5757
function getFlowerPrices(round: number, turn: number, state: State) : FlowerPrice[] {
58-
const prices = getTurns(round, turn, state).find(t => t.botPersistence)?.marketPrices
58+
const prices = getTurns(round, turn, state)[0]?.marketPrices
5959
if (prices) {
6060
return prices
6161
}

src/util/getBuyFlowerCost.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Calculates the total cost for buying flowers.
3+
* The first flower costs the current price, each additional flower costs 1 more.
4+
* The price per flower is never higher than 12.
5+
* @param currentPrice Current market price
6+
* @param count Number of flowers to buy
7+
* @returns Total cost
8+
*/
9+
export default function getBuyFlowerCost(currentPrice: number, count: number) : number {
10+
let total = 0
11+
for (let i = 0; i < count; i++) {
12+
total += Math.min(currentPrice + i, 12)
13+
}
14+
return total
15+
}

src/util/getSellFlowerRevenue.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Calculates the total revenue for selling flowers.
3+
* Before selling, the price is reduced by 1. Each additional flower sells for 1 less.
4+
* The price per flower is never lower than 1.
5+
* @param currentPrice Current market price
6+
* @param count Number of flowers to sell
7+
* @returns Total revenue
8+
*/
9+
export default function getSellFlowerRevenue(currentPrice: number, count: number) : number {
10+
let total = 0
11+
for (let i = 0; i < count; i++) {
12+
total += Math.max(currentPrice - 1 - i, 1)
13+
}
14+
return total
15+
}

src/views/RoundTurnPlayer.vue

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
<template>
22
<SideBar :navigationState="navigationState"/>
3-
<h1>{{t('player.player')}}</h1>
3+
<h1>{{t('player.player')}}: {{t('sideBar.turn', {turn})}}</h1>
44

5-
<p class="mt-4">Select your action:</p>
5+
<p class="mt-4" v-html="t('roundTurnPlayer.selectAction')"/>
66

77
<div class="actions">
88
<button class="btn btn-primary btn-lg" v-if="!showDeliveryActions" @click="showDeliveryActions = true">
9-
Make Delivery
9+
{{t('roundTurnPlayer.makeDelivery')}}
1010
</button>
1111
<div class="deliveryActions" v-if="showDeliveryActions">
1212
<button class="btn btn-primary btn-lg" v-for="floor of floors" :key="floor" @click="deliverToFloor(floor)">
13-
Delivered to Floor {{floor}}
13+
{{t('roundTurnPlayer.deliveredToFloor', {floor})}}
1414
</button>
1515
</div>
16-
<button class="btn btn-primary btn-lg" @click="next">
17-
Buy Flower
16+
<button class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#buyFlowerModal">
17+
{{t('roundTurnPlayer.buyFlower.title')}}
1818
</button>
1919
<button class="btn btn-primary btn-lg" @click="next">
20-
Other Action
20+
{{t('roundTurnPlayer.otherAction')}}
2121
</button>
2222
<div>
2323
<button class="btn btn-outline-danger btn-lg passButton" data-bs-toggle="modal" data-bs-target="#passModal">
@@ -29,6 +29,31 @@
2929
</div>
3030
</div>
3131

32+
<ModalDialog id="buyFlowerModal" :title="t('roundTurnPlayer.buyFlower.title')">
33+
<template #body>
34+
<div>{{t('roundTurnPlayer.buyFlower.selectFlower')}}</div>
35+
<div class="d-flex flex-wrap gap-2 mb-3">
36+
<button v-for="flower in allFlowers" :key="flower" type="button"
37+
class="btn flower-btn" :class="buyFlowerType === flower ? 'btn-dark' : 'btn-outline-secondary'"
38+
@click="buyFlowerType = flower">
39+
<FlowerIcon :flower="flower"/>
40+
</button>
41+
</div>
42+
<div>
43+
{{t('roundTurnPlayer.buyFlower.numberOfFlowers')}}<br/>
44+
<NumberInput v-model="buyFlowerCount" :min="1" :max="20" class="numberInput"/>
45+
</div>
46+
<div class="mt-3">
47+
<span>{{t('roundTurnPlayer.buyFlower.totalCost')}}</span>
48+
<span class="cost">$<span class="value">{{buyFlowerTotalCost}}</span></span>
49+
</div>
50+
</template>
51+
<template #footer>
52+
<button class="btn btn-primary" :disabled="!buyFlowerType || !buyFlowerCount" @click="buyFlower()" data-bs-dismiss="modal">{{t('action.ok')}}</button>
53+
<button class="btn btn-secondary" data-bs-dismiss="modal">{{t('action.cancel')}}</button>
54+
</template>
55+
</ModalDialog>
56+
3257
<ModalDialog id="passModal" :title="t('action.pass')">
3358
<template #body>
3459
<p v-html="t('roundTurnPlayer.passConfirm')"></p>
@@ -52,19 +77,25 @@ import NavigationState from '@/util/NavigationState'
5277
import FooterButtons from '@/components/structure/FooterButtons.vue'
5378
import { useStateStore } from '@/store/state'
5479
import ModalDialog from '@brdgm/brdgm-commons/src/components/structure/ModalDialog.vue'
80+
import NumberInput from '@brdgm/brdgm-commons/src/components/form/NumberInput.vue'
5581
import SideBar from '@/components/round/SideBar.vue'
5682
import DebugInfo from '@/components/round/DebugInfo.vue'
5783
import getSoloBoardPassAction, { SoloBoardPassAction } from '@/util/getSoloBoardPassAction'
5884
import SoloBoardPassActionInfo from '@/components/round/SoloBoardPassActionInfo.vue'
85+
import FlowerIcon from '@/components/structure/FlowerIcon.vue'
5986
import Player from '@/services/enum/Player'
87+
import Flower from '@/services/enum/Flower'
88+
import getBuyFlowerCost from '@/util/getBuyFlowerCost'
6089
6190
export default defineComponent({
6291
name: 'RoundTurnPlayer',
6392
components: {
6493
FooterButtons,
6594
ModalDialog,
95+
NumberInput,
6696
SideBar,
6797
SoloBoardPassActionInfo,
98+
FlowerIcon,
6899
DebugInfo
69100
},
70101
setup() {
@@ -78,8 +109,10 @@ export default defineComponent({
78109
79110
const playerDeliveryFloor = ref(navigationState.playerDeliveryFloor)
80111
const showDeliveryActions = ref(false)
112+
const buyFlowerType = ref(undefined as Flower|undefined)
113+
const buyFlowerCount = ref(1)
81114
82-
return { t, router, navigationState, state, round, turn, playerDeliveryFloor, showDeliveryActions }
115+
return { t, router, navigationState, state, round, turn, playerDeliveryFloor, showDeliveryActions, buyFlowerType, buyFlowerCount }
83116
},
84117
computed: {
85118
backButtonRouteTo() : string {
@@ -91,6 +124,13 @@ export default defineComponent({
91124
floors() : number[] {
92125
return [5,4,3,2,1]
93126
},
127+
allFlowers() : Flower[] {
128+
return this.navigationState.marketPrices.flowerOrder
129+
},
130+
buyFlowerTotalCost() : number {
131+
const price = this.navigationState.marketPrices.getPrice(this.buyFlowerType!)
132+
return getBuyFlowerCost(price, this.buyFlowerCount)
133+
},
94134
soloBoardPassAction() : SoloBoardPassAction {
95135
const { soloBoard, playerTurns, playerDeliveryFloor } = this.navigationState
96136
return getSoloBoardPassAction(soloBoard, playerTurns, playerDeliveryFloor)
@@ -105,6 +145,13 @@ export default defineComponent({
105145
this.playerDeliveryFloor = floor
106146
this.next()
107147
},
148+
buyFlower() : void {
149+
if (this.buyFlowerType && this.buyFlowerCount) {
150+
const newPrice = this.navigationState.marketPrices.getPrice(this.buyFlowerType) + this.buyFlowerCount
151+
this.navigationState.marketPrices.setPrice(this.buyFlowerType, newPrice)
152+
this.next()
153+
}
154+
},
108155
next() : void {
109156
this.nextWithPassed(false)
110157
},
@@ -153,4 +200,17 @@ export default defineComponent({
153200
justify-content: center;
154201
align-items: center;
155202
}
203+
.flower-btn {
204+
padding: 0.4rem 0.6rem;
205+
}
206+
.cost {
207+
color: darkred;
208+
margin-left: 0.25rem;
209+
.value {
210+
font-weight: bold;
211+
}
212+
}
213+
.numberInput {
214+
width: 4rem;
215+
}
156216
</style>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import getBuyFlowerCost from '@/util/getBuyFlowerCost'
2+
import { expect } from 'chai'
3+
4+
describe('util/getBuyFlowerCost', () => {
5+
it('buy 1 flower at price 5', () => {
6+
expect(getBuyFlowerCost(5, 1)).to.eq(5)
7+
})
8+
9+
it('buy 2 flowers at price 5', () => {
10+
// 5 + 6 = 11
11+
expect(getBuyFlowerCost(5, 2)).to.eq(11)
12+
})
13+
14+
it('buy 3 flowers at price 5', () => {
15+
// 5 + 6 + 7 = 18
16+
expect(getBuyFlowerCost(5, 3)).to.eq(18)
17+
})
18+
19+
it('buy 1 flower at price 1', () => {
20+
expect(getBuyFlowerCost(1, 1)).to.eq(1)
21+
})
22+
23+
it('price capped at 12', () => {
24+
// price 11: 11 + 12 = 23
25+
expect(getBuyFlowerCost(11, 2)).to.eq(23)
26+
})
27+
28+
it('price capped at 12 for multiple flowers', () => {
29+
// price 10: 10 + 11 + 12 + 12 = 45
30+
expect(getBuyFlowerCost(10, 4)).to.eq(45)
31+
})
32+
33+
it('buy at price 12', () => {
34+
// 12 + 12 + 12 = 36
35+
expect(getBuyFlowerCost(12, 3)).to.eq(36)
36+
})
37+
38+
it('buy 0 flowers', () => {
39+
expect(getBuyFlowerCost(5, 0)).to.eq(0)
40+
})
41+
})
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import getSellFlowerRevenue from '@/util/getSellFlowerRevenue'
2+
import { expect } from 'chai'
3+
4+
describe('util/getSellFlowerRevenue', () => {
5+
it('sell 1 flower at price 5', () => {
6+
// 5 - 1 = 4
7+
expect(getSellFlowerRevenue(5, 1)).to.eq(4)
8+
})
9+
10+
it('sell 2 flowers at price 5', () => {
11+
// 4 + 3 = 7
12+
expect(getSellFlowerRevenue(5, 2)).to.eq(7)
13+
})
14+
15+
it('sell 3 flowers at price 5', () => {
16+
// 4 + 3 + 2 = 9
17+
expect(getSellFlowerRevenue(5, 3)).to.eq(9)
18+
})
19+
20+
it('sell 1 flower at price 10', () => {
21+
// 10 - 1 = 9
22+
expect(getSellFlowerRevenue(10, 1)).to.eq(9)
23+
})
24+
25+
it('revenue floored at 1', () => {
26+
// price 2: sell 1 => 1, sell 2 => 1 (floored)
27+
expect(getSellFlowerRevenue(2, 2)).to.eq(2)
28+
})
29+
30+
it('revenue floored at 1 for multiple flowers', () => {
31+
// price 3: 2 + 1 + 1 + 1 = 5
32+
expect(getSellFlowerRevenue(3, 4)).to.eq(5)
33+
})
34+
35+
it('sell at price 1', () => {
36+
// price 1: each flower sells at max(0, 1) = 1
37+
// Actually: max(1 - 1 - 0, 1) = max(0, 1) = 1
38+
// max(1 - 1 - 1, 1) = max(-1, 1) = 1
39+
expect(getSellFlowerRevenue(1, 3)).to.eq(3)
40+
})
41+
42+
it('sell 0 flowers', () => {
43+
expect(getSellFlowerRevenue(5, 0)).to.eq(0)
44+
})
45+
})

0 commit comments

Comments
 (0)