Assignment 3 (30%)
This is a group assignment. You will be assigned to a group of 3 students. You must work together to complete the assignment, and submit a single solution for the group.
Task
You need to write a new R package dealr to implement card decks. The package should not implement any specific card game, but should provide the tools for creating and manipulating card decks that could be used in a variety of card games.
The package should implement a custom vctrs record class for card objects. The package should also include S3 classes for deck, hand, and deal objects.
The package must export the following user-facing functions.
card(rank, suit): A function to create acardvector with specified ranks and suits. The function should validate the inputs to ensure that they represent valid ranks and suits. Valid ranks are “A”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “10”, “J”, “Q”, and “K”. Valid suits are “Spades”, “Diamonds”, “Clubs”, and “Hearts”, or their first letters. Inputs should not be case-sensitive. The function should return acardobject that contains the specified cards.is_card(x): A function to check if an object is a card vector.card_rank(card): A function to extract the rank from a card vector.card_suit(card): A function to extract the suit from a card vector.format.card(x, ...): A method to format a card vector for printing.deck(cards): A function to create adeckobject from a vector of cards.standard_deck(n_decks = 1L): A function to create one or more standard decks of 52 playing cards. There is no need to handle jokers or other special cards. The function should return adeckobject containing all cards in the following order: Clubs (Ace to King), Diamonds (Ace to King), Hearts (Ace to King), Spades (Ace to King). (Yes, I know many card manufacturers use a different order.) Then_decksargument should allow for creating multiple decks (e.g.,standard_deck(2)would create a deck with 104 cards).hand(cards, player): A function to create ahandobject from a vector of cards.cards(x): A function to extract the cards from ahandordeckobject.shuffle_deck(deck): A function to return the shuffled deck.cut_deck(deck, n): A function to cut the deck at a specified positionn, moving the firstncards to the bottom.deal(deck, n, players, by = c("round", "batch")): A function to dealncards to each player. e.g.,deal(deck, 5, c("Muhammad", "Fan"))would deal two hands, each of five cards. Thebyargument determines whether to deal cards round-robin (one card at a time to each player) or in batches (ncards to player 1, thenncards to player 2, etc.). The function should return adealobject containing a list of the hands and the remaining deck, along with any metadata (e.g., the players’ names, the number of cards dealt, dealing style, etc.).deal()is a deterministic function where cards are dealt from the top of the deck.hands(deal): A function to extract the hands from adealobject.remaining_deck(deal): A function to extract the remaining deck from adealobject.filter_cards()methods forcard,hand, anddeckobjects. These functions should allow users to filter cards based on rank, suit, or both. For example,filter_cards(hand, suit == "Hearts")would return a newhandobject containing only the cards of the specified suit, whilefilter_cards(deck, rank %in% c("A","J","Q","K"), suit %in% c("Spades","Clubs"))would return adeckcontaining all black court cards from the input deck. Original ordering of the input cards should be preserved in the output. Multiple expressions are combined using logical AND.
You may add additional internal functions as needed to support the implementation of the user-facing functions.
Additional requirements:
cardvectors must support subsetting, concatenation, equality testing, and sorting. When comparing cards for equality, both rank and suit must match. When sorting cards, order first by suit (Clubs < Diamonds < Hearts < Spades), then by rank (A < 2 < 3 < … < 10 < J < Q < K).as.data.frame()methods should be implemented to convertcard,hand, anddeckobjects to data frames with appropriate columns.print()methods should be implemented forcard,hand,deckanddealobjects to display them in a user-friendly way.cards()should be implemented as an S3 generic function with methods forhandanddeckobjects.filter_cards()should be implemented using tidy evaluation to allow for flexible filtering criteria. The function should validate the filtering criteria and handle cases where no cards match the criteria appropriately (e.g., by returning an emptyhandobject if the input is ahand). You may userlangfunctions, but you should not usedplyrin the implementation offilter_cards(). Unit tests should cover various filtering scenarios, including edge cases where no cards match the criteria.- The order of cards must always be preserved unless explicitly modified (e.g., by
shuffle_deck()orcut_deck()) - All user-facing functions should be fully documented using roxygen2, with examples that can be run in the documentation. The package should also include unit tests for all exported functions using
testthat. The unit tests should cover a variety of cases, including edge cases and invalid inputs. - There should be a vignette describing how to use the package.
- The package should not implement game-specific logic such as poker-hand ranking, blackjack scoring, trick-taking rules, or winner determination.
- The package should not involve any user interactivity; all functions should operate on their inputs and return outputs without requiring user input during execution.
- The package should not include any plotting or graphical functions; it should focus solely on the data structures and operations for card decks.
Units tests must include at least:
- invalid ranks and suits error;
standard_deck(n_decks = 2L)has 104 cards;shuffle_deck()preserves the set of cards and the number of cards;cut_deck()preserves all cards and changes the order as documented;deal()does not modify the input deck;deal()returns the correct number of hands;- each hand has the requested number of cards;
- the remaining deck has the correct number of cards;
- round and batch dealing produce the documented positions;
- dealing too many cards produces an informative error;
filter_cards()works with unquoted expressions and preserves the input type
Marks will be awarded for clean and efficient code, and for good design.
Notes
The package will be developed on GitHub Classroom. Each team will be given a private repository for the assignment. The state of the repository at the time of the deadline will be counted as your submission. Commits after the deadline will be ignored.
Marks will be allocated for regular commits throughout the assignment period. Commits should have meaningful messages that reflect the changes made. Do not combine multiple changes into a single commit. Marks will be deducted for poor commit practices (e.g., large infrequent commits with vague messages).
Each team member must contribute at least six substantive commits across multiple days. A substantive commit is one that adds or modifies meaningful code, documentation, or tests. Minor commits (e.g., fixing typos, formatting) will not count towards this requirement. If a team member has not contributed adequately, that team member will receive a mark deduction of up to 75% of the total mark awarded.
Generative AI tools may be used in guided ways in this assessment, but you must explain how it was used, including prompts where relevant. Each assignment must include an AI statement named ‘AI_statement.md’ with a section for each student in the team. Evidence of AI use that is not mentioned in the statement will result in penalties being applied. Any work submitted for a mark must:
- represent a sincere demonstration of your human efforts, skills and subject knowledge that you will be accountable for;
- adhere to the guidelines for AI use set for the assessment task;
- reflect the University’s commitment to academic integrity and ethical behaviour.
Inappropriate AI use and/or AI use without acknowledgement will be considered a breach of academic integrity. See Learn HQ for more information.
Marking rubric
Total: 100 marks (scaled to 30%)
1. Is your package well-structured and cleanly built? (10 marks)
- package installs and passes
R CMD checkwithout errors or warnings - correct package structure (
DESCRIPTION,NAMESPACE, R source files, tests, vignette) - all required functions exported with dependencies declared correctly
- clear, general-purpose design with no global state, interactivity, plotting, or game-specific logic
2. Does your card class work correctly? (15 marks)
cardimplemented as a genuine vctrs record class with rank and suit fields- constructor validates ranks and suits, handles case-insensitive input and one-letter suit abbreviations, supports vectorisation and recycling
- accessors (
is_card(),card_rank(),card_suit()) andformat.card()work correctly - card vectors support subsetting, concatenation, equality testing, and sorting in the required order
3. Do your deck, hand, and deal classes work correctly? (10 marks)
deck()andhand()construct valid S3 objects with appropriate validation and metadatadeal()returns a well-structured object with hands, remaining deck, and metadata- accessors
cards(),hands(), andremaining_deck()work correctly - card order is preserved throughout
4. Do your deck manipulation and dealing functions work correctly? (10 marks)
standard_deck()produces the correct cards in the required order, including multiple decksshuffle_deck()andcut_deck()work correctly and do not mutate inputsdeal()supports both round and batch dealing, does not modify the input deck, and errors informatively on invalid inputs or insufficient cards
5. Does your filter_cards() function work correctly? (15 marks)
- uses tidy evaluation (e.g.,
rlang) without delegating todplyr - supports unquoted expressions on
rankandsuit, with multiple expressions combined using AND - preserves the input type and original card order
- handles empty results and invalid expressions correctly
6. Do your print and data frame methods work correctly? (10 marks)
print()methods forcard,deck,hand, anddealare user-friendly and do not expose internalsas.data.frame()methods forcard,deck, andhandreturn correctly structured data frames- methods handle empty objects and edge cases without error
7. Do you have good unit tests? (10 marks)
- tests cover all required cases listed in the assignment instructions
- tests for class behaviour (subsetting, concatenation, equality, sorting, accessors)
- tests for edge cases and invalid inputs
8. Is your documentation helpful? (10 marks)
- all exported functions documented with roxygen2, including runnable examples
- vignette explaining the full package workflow
9. Did the group use GitHub and AI properly? (10 marks)
- regular, meaningful commits from all group members throughout the assignment
- good commit messages
AI_statement.mdwith a section for each student
Marks may be adjusted for serious package failures, academic integrity issues, or inadequate individual contribution as described in the assignment instructions.
Due: 2 June 2026
Join GitHub Classroom